Доступен — Локальные цифровые продукты
HCA · Studio
TR Контакты ↗
Заметка 02 · Отладка

«Ваша корзина пуста» и отложенный JavaScript

Товар добавляется, счётчик корзины растёт — а страница оформления говорит, что корзина пуста. Самая частая причина не кэш, а плагин скорости, откладывающий JavaScript, от которого зависит блочное оформление.

Симптом
Блочное оформление показывает пустую корзину
Причина
Сплошной defer
Время
Исправление за 15 минут
Короткий ответ

Блочные корзина и оформление WooCommerce зависят от объекта wp и пакетов скриптов wp-*. Когда плагин скорости откладывает все скрипты без исключений, эти пакеты выполняются в неверном порядке, в консоли возникает wp is not defined, и блок отрисовывает состояние пустой корзины. Решение — исключить скрипты ядра WordPress и блоков WooCommerce из правила отложенной загрузки, а не чистить корзину или кэш.

Симптомы

  • «Добавить в корзину» работает, счётчик в шапке растёт.
  • Страница корзины выглядит пустой или не отрисовывается вовсе.
  • Оформление сообщает «Ваша корзина пуста».
  • Проблема есть не у всех — у авторизованного администратора она обычно не воспроизводится.
  • В журнале ошибок PHP ничего нет.

Последние два пункта важны. У администратора проблема не видна, потому что большинство плагинов скорости отключают оптимизацию для авторизованных пользователей. Владелец сайта узнаёт о сбое позже всех: он происходит только у реальных клиентов.

Ложные диагнозы

  • Очистка кэша. Может показаться, что помогло, потому что при первой загрузке меняется тайминг скриптов; через несколько минут повторяется.
  • Исключение оформления из кэша. Верная настройка, которую и так следует сделать, но причина не в ней.
  • Правка настроек сессий и cookie. Делается в предположении, что дело в сессии; но корзина на сервере верна, проблема на экране.
  • Переустановка темы или WooCommerce. Потеря времени.

Общая ошибка — искать проблему на сервере. Сервер знает корзину правильно, доказательство — растущий счётчик.

Верный диагноз: консоль

Откройте страницу оформления без авторизации (в приватном окне) и посмотрите в консоль браузера.

Uncaught ReferenceError: wp is not defined
    at checkout-blocks.js:1
Uncaught TypeError: Cannot read properties of undefined (reading 'createElement')

Если эти строки есть, диагноз поставлен. Корзина в порядке, сервер в порядке; JavaScript, который рисует страницу, умирает до запуска.

Дополнительная проверка: посмотрите теги <script> в исходном коде. Если скрипты ядра (wp-polyfill, wp-element, wp-data) тоже помечены defer, причина подтверждена.

Первопричина

Скрипты WordPress зависят друг от друга, и этот порядок объявляется через wp_enqueue_script. Обычно браузер его сохраняет.

Атрибут defer заставляет скрипты выполняться после разбора HTML. Спецификация говорит, что отложенные скрипты выполняются в порядке документа, но при динамически добавляемых скриптах, условной загрузке и встроенном коде эта гарантия на практике ломается. В частности:

  • Встроенные скрипты нельзя отложить. Блоки WooCommerce передают настройки через встроенные <script>. Этот код выполняется сразу, а wp-element, от которого он зависит, отложен. Порядок инвертируется.
  • Модули и классические скрипты смешиваются. Пропуск разных моделей загрузки через одно правило делает тайминг непредсказуемым.

Результат: код, обращающийся к wp, выполняется до его определения, выбрасывает ошибку, и блок на React не монтируется. Не смонтировавшись, он показывает состояние по умолчанию — пустую корзину.

Когда я доводил эту ошибку до первопричины на реальном сайте, источником оказалось правило сплошного defer во вспомогательном плагине темы: несколько строк, добавлявших defer всем скриптам сайта без списка исключений.

Исправление

add_filter( 'script_loader_tag', function ( $tag, $handle ) {

    // Идентификаторы, которые нельзя откладывать
    $skip_prefixes = array( 'wp-', 'wc-', 'woocommerce' );
    $skip_exact    = array( 'jquery', 'jquery-core', 'jquery-migrate' );

    foreach ( $skip_prefixes as $prefix ) {
        if ( 0 === strpos( $handle, $prefix ) ) {
            return $tag;
        }
    }
    if ( in_array( $handle, $skip_exact, true ) ) {
        return $tag;
    }

    // На корзине и оформлении не откладываем ничего
    if ( function_exists( 'is_checkout' ) && ( is_checkout() || is_cart() ) ) {
        return $tag;
    }

    if ( false !== strpos( $tag, ' defer' ) || false === strpos( $tag, ' src=' ) ) {
        return $tag;
    }

    return str_replace( ' src=', ' defer src=', $tag );

}, 10, 2 );

При использовании готового плагина та же логика применяется через его настройки: в разделе отложенной загрузки/объединения JavaScript есть список исключений. Добавьте туда идентификаторы, начинающиеся на wp-, wc- и woocommerce, и по возможности полностью исключите корзину и оформление из оптимизации.

Проверка

  1. В приватном окне (без авторизации) добавьте товар в корзину.
  2. Откройте оформление; корзина должна быть заполнена.
  3. Консоль должна быть чистой — wp is not defined обязан исчезнуть.
  4. Проверьте полноту списка способов оплаты. Та же ошибка скрывает собственные шлюзы из блочного оформления.
  5. Очистите кэш и повторите — исправление должно работать и при первом кэшированном визите.

Итог

«Ваша корзина пуста» — обычно не ошибка корзины, а ошибка порядка загрузки скриптов. Сервер знает корзину; JavaScript, который её нарисует, не может запуститься.

Общее правило: оптимизация скорости не применяется без исключений. Сплошные правила откладывания, объединения или задержки должны исключать скрипты ядра WordPress и платёжный процесс. Ценой скорости не должны быть продажи.

Быстрая диагностика
Симптом
Счётчик корзины растёт, но оформление показывает её пустой
Ключевая подсказка
У авторизованного администратора проблемы нет, в приватном окне есть
Ошибка в консоли
Uncaught ReferenceError: wp is not defined
Первопричина
defer добавлен всем скриптам без исключений
Решение
Исключить идентификаторы wp-, wc-, woocommerce из откладывания
Доп. защита
Полностью отключить оптимизацию на корзине и оформлении
Смежный симптом
Собственный платёжный шлюз не появляется в блочном оформлении
Частые вопросы

Об этой ошибке.

Товар добавляется, но оформление говорит, что корзина пуста — почти всегда почему?

Сервер почти наверняка знает корзину верно, но JavaScript, который её отрисовывает, не может выполниться. Блочные корзина и оформление зависят от объекта wp; когда плагин скорости откладывает все скрипты, объект не определяется вовремя, и блок рисует себя как пустую корзину.

Почему проблема только у неавторизованных посетителей?

Потому что большинство плагинов скорости отключают кэширование и оптимизацию скриптов для авторизованных пользователей. Просматривая сайт администратором, вы не получаете оптимизации и не видите ошибки. Поэтому тестировать нужно в приватном окне, без авторизации.

Помогает ли очистка кэша?

Нет. Может показаться, что помогло, потому что при первой загрузке меняется тайминг скриптов, но проблема вскоре возвращается. Постоянное решение — добавить в правило откладывания список исключений для скриптов ядра WordPress и WooCommerce.

Стоит ли вообще отключить откладывание JavaScript ради скорости?

Не нужно. Откладывание даёт реальный выигрыш на страницах вне платёжного процесса. Важно не применять его без исключений: освободить скрипты с префиксами wp-, wc- и woocommerce и полностью отключить оптимизацию на корзине и оформлении.

Доступность и смета

Виден в локальном поиске.
Убедителен в продукте.