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

Быстрые картинки на витрине - размеры, ленивая загрузка, WebP

Ускоряем витрину за счёт картинок: копии под место вывода, ленивая загрузка, набор размеров под экраны и современный формат изображения.

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

Тяжёлые картинки - самая частая причина медленной витрины после запросов к базе. Одна фотография с фотоаппарата весит больше, чем весь остальной код страницы вместе с оформлением.

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

Ленивая загрузка помогает не всегда и при неверной настройке делает только хуже. Картинка первого экрана загружается сразу и без отсрочки, иначе посетитель видит пустое место вместо главного изображения.

Шаги

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

Решение

Считаем вес картинок страницы:

Окно терминала
curl -s https://example.com/catalog/ | grep -oE 'src="[^"]+\.(jpg|png)"' | \
cut -d'"' -f2 | head -40 > /tmp/imgs.txt
wc -l /tmp/imgs.txt # сколько картинок вообще на странице
# дальше их вес смотрят панелью разработчика в браузере, вкладкой сети

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

Выводим копию под место вывода:

$img = CFile::ResizeImageGet($item['PREVIEW_PICTURE'], ['width' => 400, 'height' => 400],
BX_RESIZE_IMAGE_PROPORTIONAL, true); // копия создаётся один раз, при первом показе
echo '<img src="' . $img['src'] . '" width="400" height="400" alt="' . $item['NAME'] . '">';
// ширина и высота в разметке убирают скачок вёрстки при загрузке

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

Откладываем дальние картинки:

<img src="<?= $img['src'] ?>" loading="<?= $index < 4 ? 'eager' : 'lazy' ?>"
width="400" height="400" alt="<?= $item['NAME'] ?>">
<!-- первые карточки грузятся сразу: они на первом экране -->

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

Даём набор размеров под разные экраны:

$small = CFile::ResizeImageGet($id, ['width' => 400, 'height' => 400], BX_RESIZE_IMAGE_PROPORTIONAL, true);
$large = CFile::ResizeImageGet($id, ['width' => 800, 'height' => 800], BX_RESIZE_IMAGE_PROPORTIONAL, true);
echo '<img src="' . $small['src'] . '" srcset="' . $small['src'] . ' 400w, ' . $large['src'] . ' 800w"'
. ' sizes="(max-width: 768px) 50vw, 400px" width="400" height="400" alt="">';

Набор размеров нужен там, где картинка занимает разную ширину. Телефон получит лёгкий файл, а экран с высокой плотностью точек - крупный, и обе картинки будут выглядеть одинаково чётко.

Отдаём современный формат по заголовку запроса:

location ~* ^(/upload/.+)\.(jpe?g|png)$ {
add_header Vary Accept;
try_files $1.webp $uri =404; # файл нового формата отдаём только если он есть
}
# сами файлы готовят при загрузке картинки или отдельным заданием по каталогу

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

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

Список товаров грузится минуту на телефоне.

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

Главная картинка появляется с задержкой.

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

Каталог уменьшенных копий вырос до десятков гигабайтов.

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

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

У картинок не указаны ширина и высота прямо в разметке страницы. Браузер узнаёт размер только после загрузки файла и переставляет блоки.

Часть посетителей видит битые картинки.

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

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

Где хранятся уменьшенные копии?

В отдельном каталоге загрузок, рядом с исходными файлами. Их не переносят при копировании сайта: они создаются заново при первом показе.

Нужно ли сжимать картинки до загрузки?

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

Сколько размеров копий держать?

Два-три на весь сайт: плитка, детальная страница и увеличенный просмотр. Каждый новый размер удваивает объём каталога копий и работу при перегенерации.

Помогает ли ленивая загрузка на детальной странице?

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

Можно ли отдавать картинки через внешнюю сеть доставки?

Да, и для большого каталога это разумно. Настройка делается на стороне сервиса, а сайт продолжает отдавать те же адреса файлов.

Смежное

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