Открыть список
Как стать автором
Обновить

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

Блог компании Mail.ru GroupВысокая производительностьРазработка веб-сайтовКлиентская оптимизацияБраузеры
Перевод
Tutorial
Автор оригинала: Malte Ubl

В этой статье я расскажу про 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
<img loading="lazy" decoding="async" style="background-size: cover; background-image: none;" src="/img/remote/ZiClJf.avif" alt="Sample image illustrating the techniques outlined in this post." width="4032" height="2268">


Методики оптимизации


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


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

<style>
 img {
   max-width: 100%;
   height: auto;
 }
</style>
<!-- Providing width and height is more important than ever. -->
<img height="853" width="1280" … />

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


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

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


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

<style>
 /* This probably only makes sense for images within the main scrollable area of your page. */
 main img {
   /* Only render when in viewport */
   content-visibility: auto;
 }
</style>

AVIF


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

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

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

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

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

<picture>
 <source
   sizes="(max-width: 608px) 100vw, 608px"
   srcset="
     /img/Z1s3TKV-1920w.avif 1920w,
     /img/Z1s3TKV-1280w.avif 1280w,
     /img/Z1s3TKV-640w.avif   640w,
     /img/Z1s3TKV-320w.avif   320w
   "
   type="image/avif"
 />
 <!-- snip lots of other stuff -->
 <img />
</picture>

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


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

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

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


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

<source
 sizes="(max-width: 608px) 100vw, 608px"
 srcset="
   /img/Z1s3TKV-1920w.webp 1920w,
   /img/Z1s3TKV-1280w.webp 1280w,
   /img/Z1s3TKV-640w.webp   640w,
   /img/Z1s3TKV-320w.webp   320w
 "
 type="image/webp"
/>
<source
 sizes="(max-width: 608px) 100vw, 608px"
 srcset="
   /img/Z1s3TKV-1920w.jpg 1920w,
   /img/Z1s3TKV-1280w.jpg 1280w,
   /img/Z1s3TKV-640w.jpg   640w,
   /img/Z1s3TKV-320w.jpg   320w
 "
 type="image/jpeg"
/>

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


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

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

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


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

<img loading="lazy" … />

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


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

<img decoding="async" … />

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


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

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


Несколько замечаний по реализации:

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

<img
 style="
     …
     background-size: cover;
     background-image:
       url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns=\'http%3A//www.w3.org/2000/svg\'

xmlns%3Axlink=\'http%3A//www.w3.org/1999/xlink\' viewBox=\'0 0 1280 853\'%3E%3Cfilter id=\'b\' color-interpolation-filters=\'sRGB\'%3E%3CfeGaussianBlur stdDeviation=\'.5\'%3E%3C/feGaussianBlur%3E%3CfeComponentTransfer%3E%3CfeFuncA type=\'discrete\' tableValues=\'1 1\'%3E%3C/feFuncA%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter=\'url(%23b)\' x=\'0\' y=\'0\' height=\'100%25\' width=\'100%25\'
       xlink%3Ahref=\'data%3Aimage/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAIAAACepSOSAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAs0lEQVQI1wGoAFf/AImSoJSer5yjs52ktp2luJuluKOpuJefsoCNowB+kKaOm66grL+krsCnsMGrt8m1u8mzt8OVoLIAhJqzjZ2tnLLLnLHJp7fNmpyjqbPCqLrRjqO7AIeUn5ultaWtt56msaSnroZyY4mBgLq7wY6TmwCRfk2Pf1uzm2WulV+xmV6rmGyQfFm3nWSBcEIAfm46jX1FkH5Djn5AmodGo49MopBLlIRBfG8yj/dfjF5frTUAAAAASUVORK5CYII=\'%3E%3C/image%3E%3C/svg%3E');
   "
 …
/>

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


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

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

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


Полезный инструмент, реализующий все описанные оптимизации: eleventy-high-performance-blog
Теги:загрузка картинокоптимизация изображенийникто не читает теги
Хабы: Блог компании Mail.ru Group Высокая производительность Разработка веб-сайтов Клиентская оптимизация Браузеры
Всего голосов 29: ↑29 и ↓0 +29
Просмотры16.4K

Комментарии 65

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
Местоположение
Россия
Сайт
team.mail.ru
Численность
5 001–10 000 человек
Дата регистрации
Представитель
Павел Круглов

Блог на Хабре