[Перевод] Максимально оптимизированная веб-загрузка изображений в 2021 году

_pena6zde6vdpgopmo6wk3z8u6w.jpeg

В этой статье я расскажу про 8 методик оптимизации загрузки изображений, которые уменьшают необходимую пропускную способность сети и нагрузку на процессор при выводе на экран. Приведу примеры аннотированного HTML, чтобы вам было легче воспроизвести. Какие-то методики уже давно известны, а какие-то появились относительно недавно. В идеале, ваш любимый механизм публикации веб-документов (например, CMS, генератор статичных сайтов или фреймворк для веб-приложений) должен всё это реализовывать из коробки.
В совокупности методики оптимизируют все элементы Google Core Web Vitals с помощью:
  • минимизации основных проблем содержимого (Largest Contentful Paint (LCP)) за счёт уменьшения размеров, кеширования и ленивой загрузки;
  • сохранения нулевого накопительного сдвига макета (Cumulative Layout Shift (CLS));
  • уменьшения задержки первого ввода (First Input Delay (FID)) за счёт снижения потребления процессора (для основного потока исполнения).

Чтобы увидеть в действии работу всех методик, посмотрите на исходный код загрузки этого изображения:
https://www.industrialempathy.com/img/remote/ZiClJf.jpg
Sample image illustrating the techniques outlined in this post.

Отзывчивый макет


Это понятная методика позволяет изображению занимать доступное горизонтальное пространство с сохранением соотношения сторон. В 2020-м браузеры научились резервировать корректное количество вертикального пространства под изображение до его загрузки, если в элементе img указаны атрибуты width и height. Это позволяет избежать накопительного сдвига макета.




Ленивая отрисовка


Вторая методика сложнее. Новый CSS-атрибут content-visibility: auto говорит браузеру не думать о размещении изображения, пока оно не будет готово. У этого подхода несколько достоинств, главное из которых в том, что пока браузер не получит размытое изображение-заглушку или само изображение, он не будет его декодировать, экономя ресурсы процессора.

Больше не нужен contain-intrinsic-size


В более ранней версии статьи объяснялось, как с помощью contain-intrinsic-size избежать эффекта CLS при использовании content-visibility: auto. Но в Chromium 88 это больше не нужно в случае с изображениями, для которых указаны width и height. По состоянию на 27 января 2021 года content-visibility: auto ещё не реализован в других браузерных движках, вероятно, они последуют примеру Chromium. Так что да, теперь с этим гораздо проще!


AVIF


AVIF — самый свежий графический формат, который получил поддержку в браузерах. Сейчас он поддерживается в Chromium и по флагу в Firefox. Safari с ним пока не работает, но поскольку Apple является участником группы, разработавшей формат, в будущем этот браузер тоже должен поддерживать AVIF.

Этот формат примечателен тем, что он значительно превосходит JPEG. И этим выгодно отличается от формата WebP, изображения в котором не всегда меньше JPEG и который может повышать потребление ресурсов из-за отсутствия поддержки прогрессирующей загрузки.

Для реализации прогрессирующего расширения для AVIF можно воспользоваться элементом picture.

Фактически элемент img вложен в picture. Это может запутать, потому что img иногда называют запасным решением для браузеров, не поддерживающих picture, но по сути этот элемент помогает лишь с выбором src, а своего макета у него нет. Отрисуется именно элемент img, к нему вы и будете применять стиль.

До недавнего времени было довольно сложно внедрять на серверной стороне AVIF-изображения, но свежие версии библиотек вроде sharp сильно облегчили эту задачу.


 
 
 


Загрузка правильного количества пикселей


В приведённом выше коде есть атрибуты srcset и sizes. С помощью селектора w они говорят браузеру, какой нужно брать URL в зависимости от физического количества пикселей, которое потребуется для отрисовки изображения на конкретном устройстве. Это количество зависит от ширины изображения, которая вычисляется на основе атрибута sizes (представляющего собой выражение из медиа-запроса).

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

Запасное решение


Браузерам, которые поддерживают только старые форматы изображений, можно с помощью srcset предоставить больше исходных элементов:



Кеширование и неизменяемые URL


Встраивайте в URL изображения хеш количества байтов, которые занимает картинка. В примере выше я сделал это с помощью Z1s3TKV. При изменении изображения поменяется и URL, а значит вы можете применять бесконечное кеширование картинок. Кеширующие заголовки должны выглядеть так: cache-control: public,max-age=31536000,immutable.

immutable — семантически правильное значение cache-control, но сегодня оно мало поддерживается браузерами (я смотрю на тебя, Chrome). max-age=31536000 — запасной способ кеширования в течение года. public нужен для того, чтобы ваш CDN кешировала изображение и доставляла его с границы сети. Но этот подход можно использовать только в том случае, если он не нарушает ваших правил соблюдения приватности.

Ленивая загрузка


Добавив loading=«lazy» в элемент img мы говорим браузеру начать извлекать изображение лишь тогда, когда оно готово быть отрисовано.


Асинхронная расшифровка


Добавив decoding=«async» в элемент img мы разрешаем браузеру расшифровывать изображение вне основного потока, чтобы эта процедура не помешала пользователю. Заметных недостатков у этого решения быть не должно, за исключением того, что оно не всегда применима по умолчанию в старых браузерах.


Размытая заглушка


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

https://www.industrialempathy.com/img/blurry.svg

c8e7f82eb52f7b1da723413ee213b136.jpg

Несколько замечаний по реализации:
  • Заглушка инлайнится как background-image изображения. Эта методика позволяет отказаться от второго HTML-элемента буквально прячет заглушку, когда загружается основное изображение, для этого не нужен JavaScript.
  • URI данных основного изображения обёртывается в URI данных SVG-картинки. Это делается потому, что размытие выполняется на уровне SVG, а не с помощью CSS-фильтра. То есть размытие выполняется однократно для каждого изображения при растеризации SVG, а не для каждого макета. Это экономит ресурсы процессора.


(Опциональная) JavaScript-оптимизация


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

 document.body.addEventListener(
   "load",
   (e) => {
     if (e.target.tagName != "IMG") {
       return;
     }
     // Remove the blurry placeholder.
     e.target.style.backgroundImage = "none";
   },
   /* capture */ true
 );


Дополнительно


Полезный инструмент, реализующий все описанные оптимизации: eleventy-high-performance-blog

© Habrahabr.ru