Перейти к содержимому

Ускорение карточки товара - состав блоков, отложенная загрузка

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

Что нужно знать заранее

Карточка товара обрастает новыми блоками совершенно незаметно для команды. Рекомендации, отзывы, наличие по складам, просмотренные товары и баннеры добавляются по одному, а тормозить начинает страница целиком.

Далеко не всё, что есть на странице, нужно посетителю сразу. Блоки ниже первого экрана можно загрузить отдельным запросом после отрисовки, и посетитель этого даже не заметит.

Замер делают до правок и обязательно повторяют после них. Число запросов к базе и время сборки страницы дают честное сравнение, а ощущение «стало быстрее» проверку не проходит.

Шаги

  1. Замерить число запросов и время сборки карточки товара на живых данных каталога.
  2. Разобрать страницу на блоки и оценить вклад каждого в это время.
  3. Вынести тяжёлые блоки ниже первого экрана в отдельный запрос после первой отрисовки.
  4. Закрыть кэшем те блоки, которые одинаковы для всех посетителей витрины.
  5. Повторить замер на второй загрузке страницы и сравнить полученные числа с исходными.

Решение

Считаем запросы и время сборки:

$conn = \Bitrix\Main\Application::getConnection();
$conn->startTracker();
$APPLICATION->IncludeComponent('bitrix:catalog.element', '', $params);
printf("запросов: %d, время: %.2f c\n", count($conn->getTracker()->getQueries()),
$conn->getTracker()->getTime());
$conn->stopTracker();

Число запросов важнее секунд на стенде разработчика. Сотня запросов в карточке почти всегда означает выборку в цикле, и она заметна на боевом сервере под нагрузкой.

Выносим тяжёлый блок в отдельный запрос:

const box = document.querySelector('#recommendations');
new IntersectionObserver(([entry], observer) => {
if (!entry.isIntersecting) { return; }
fetch('/local/ajax/recommendations.php?id=' + box.dataset.id)
.then(r => r.text()).then(html => { box.innerHTML = html; });
observer.disconnect();
}).observe(box);

Блок грузится в момент, когда посетитель до него доскроллил. Для рекомендаций, отзывов и просмотренных товаров это правильное поведение: их видят далеко не все посетители.

Кэшируем блок с тегом каталога:

$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(3600, 'card_extra_' . $productId, '/vendor/card')) {
$data = $cache->getVars();
} elseif ($cache->startDataCache()) {
$tagged = \Bitrix\Main\Application::getInstance()->getTaggedCache();
$tagged->registerTag('iblock_id_' . $iblockId);
$data = collectExtraBlocks($productId);
$cache->endDataCache($data);
}

Готовим картинки к быстрой отрисовке:

$thumb = \CFile::ResizeImageGet($fileId, ['width' => 700, 'height' => 700],
BX_RESIZE_IMAGE_PROPORTIONAL, true);
?><img src="<?= $thumb['src'] ?>" width="700" height="700" loading="lazy" alt=""><?php
// размеры в разметке убирают скачок вёрстки, ленивая загрузка экономит трафик

Проверяем результат на второй загрузке:

Окно терминала
curl -s -o /dev/null -w 'первая: %{time_total}\n' https://example.com/catalog/product/
curl -s -o /dev/null -w 'вторая: %{time_total}\n' https://example.com/catalog/product/
# первая загрузка собирает кэш, вторая показывает обычное время для посетителя

Типичные проблемы

Карточка выполняет сотни запросов к базе данных.

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

Страница ждёт ответа внешнего сервиса рекомендаций.

Блок рекомендаций собирается прямо в момент отрисовки страницы и без всякого таймаута. Такие блоки грузят отдельным запросом и жёстко ограничивают время ожидания ответа.

После включения кэша посетители видят чужие цены.

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

Замер на стенде показывает быстро, а на бою медленно.

На стенде сотня товаров, а на боевом сайте десятки тысяч и живая нагрузка. Замеряют на копии боевых данных или прямо на бою в спокойное время.

Вёрстка прыгает при загрузке картинок товара.

У изображений не заданы размеры, и браузер узнаёт их только после загрузки. Ширину и высоту картинки указывают в разметке вместе с ленивой загрузкой.

Частые вопросы

С чего начинать ускорение карточки?

С замера числа запросов и времени сборки на живых данных каталога. Без этих чисел любые правки остаются догадками о причине.

Что можно грузить отдельным запросом?

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

Как кэшировать блоки с ценами?

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

Помогает ли композит на карточке?

Помогает, но требует аккуратности: цена и наличие обязаны быть динамическими. Иначе покупатель увидит числа, собранные для другого посетителя.

Сколько запросов считается нормой?

Ориентир для карточки - несколько десятков, а не сотни. Точное число зависит от проекта, но сотня почти всегда означает выборку в цикле.

Смежное

Первоисточник