---
layout: post
title: Предварительная загрузка адаптивных изображений
subhead: Начиная с Chrome 73 адаптивные изображения можно комбинировать с link rel="preload", чтобы ускорить их загрузку.
authors:
  - yoavweiss
description: Узнайте о новых захватывающих возможностях предварительной загрузки адаптивных изображений, позволяющих повысить комфорт пользователей.
hero: image/admin/QDCTiiyXE11bYSZMP3Yt.jpg
alt: Стена с висящими на ней фоторамками различных размеров.
date: 2019-09-30
tags:
  - blog
  - performance
  - images
feedback:
  - api
---

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

## Краткий обзор адаптивных изображений

Представьте, что вы просматриваете веб-страницу на экране шириной в 300 пикселей и она загружает изображение, имеющее ширину 1500 пикселей. Это приведет к чрезмерному расходу мобильного трафика, поскольку экран устройства не может отобразить изображение с таким разрешением. В идеале браузер должен загрузить версию изображения с шириной *чуть больше* ширины экрана, например 325 пикселей, чтобы обеспечить высокую четкость без избыточного расхода трафика. Кроме того, такое изображение будет быстрее загружаться. [Адаптивные изображения](/serve-responsive-images/#serve-multiple-image-versions) позволяют браузеру загружать различные ресурсы в зависимости от параметров устройства. Если вы не используете [CDN для доставки изображений](/image-cdns/), для каждого изображения необходимо сгенерировать версии разных размеров и указать их в атрибуте `srcset`. Параметр `w` сообщает браузеру ширину каждой версии. В зависимости от параметров устройства браузер выбирает наиболее подходящую версию:

```html
<img src="small.jpg" srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 1500w" alt="…">
```

## Краткий обзор предварительной загрузки

Механизм [предварительной загрузки](/preload-critical-assets) позволяет сообщать браузеру о критически важных ресурсах, которые необходимо загружать в первую очередь, еще до их обнаружения в HTML. Это особенно полезно для ресурсов, обнаружение которых занимает много времени, таких как шрифты в таблицах стилей, фоновые изображения, а также ресурсы, загружаемые из скриптов.

```html
<link rel="preload" as="image" href="important.png">
```

## Адаптивные изображения + предварительная загрузка = ускоренная загрузка изображений

Адаптивные изображения и предварительная загрузка были доступны уже несколько лет, но в то же время чего-то не хватало: отсутствовала возможность использовать предварительную загрузку для адаптивных изображений. [Начиная с Chrome 73](https://developers.google.com/web/updates/2019/03/nic73#more) браузер может выполнять предварительную загрузку наиболее подходящего из изображений, указанных в атрибуте `srcset`, еще до обнаружения тега `img`.

В зависимости от структуры сайта это может значительно ускорить отображение изображений. Мы провели тесты на сайте, который использует JavaScript для отложенной загрузки адаптивных изображений. Благодаря предварительной загрузке изображения стали загружаться на 1,2 секунды быстрее.

{% Aside %}

Адаптивные изображения [поддерживаются во всех современных браузерах](https://developer.mozilla.org/docs/Web/HTML/Element/img#Browser_compatibility), в то время как их предварительная загрузка [поддерживается только в браузерах на основе Chromium](https://developer.mozilla.org/docs/Web/HTML/Preloading_content#Browser_compatibility).

{% endAside %}

## `imagesrcset` и `imagesizes`

Чтобы обеспечить поддержку предварительной загрузки адаптивных изображений, мы добавили новые атрибуты элемента `<link>`: `imagesrcset` и `imagesizes`. Они используются в тегах `<link rel="preload">`, и их синтаксис совпадает с атрибутами `srcset` и `sizes` тега `<img>`.

Например, если необходимо обеспечить предварительную загрузку следующего адаптивного изображения:

```html
<img src="wolf.jpg" srcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w" sizes="50vw" alt="A rad wolf">
```

…вы можете добавить в раздел `<head>` следующий HTML-код:

```html
<link rel="preload" as="image" href="wolf.jpg" imagesrcset="wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w" imagesizes="50vw">
```

Это позволяет инициировать запрос с использованием логики выбора ресурсов, соответствующей значениям атрибутов `srcset` и `sizes`.

## Сценарии использования

### Предварительная загрузка динамически подставляемых адаптивных изображений

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

Эту проблему можно изучить на примере сайта с динамически загружаемой галереей изображений:

1. Откройте в новой вкладке [сайт с примером](https://responsive-preload.glitch.me/no_preload.html).

{% Instruction 'devtools-network', 'ol' %}

1. В раскрывающемся списке **Ограничение** выберите **3G (высокая скорость)**.

2. Снимите флажок **Отключить кеш**.

3. Обновите страницу.

<figure>{% Img src="image/admin/cyocwRmB3XlfY26vUZ5h.png", alt="Скриншот панели «Сеть» в Chrome DevTools.", width="800", height="481" %} <figcaption>По ступенькам на диаграмме видно, что загрузка изображений начинается только после завершения выполнения скрипта, из-за чего возникает нежелательная задержка, прежде чем пользователь сможет увидеть изображение.</figcaption></figure>

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

<figure>{% Img src="image/admin/rIRdFypLWf1ljMaXCVCs.png", alt="Скриншот панели «Сеть» в Chrome DevTools.", width="800", height="481" %} <figcaption>На диаграмме видно, что изображение начинает загружаться одновременно со скриптом, что позволяет избежать нежелательной задержки и ускоряет его отображение.</figcaption></figure>

Чтобы увидеть прирост, который дает предварительная загрузка, вы можете ознакомиться с аналогичной динамически загружаемой галереей изображений, но [использующей предварительную загрузку первого изображения](https://responsive-preload.glitch.me/preload.html), выполнив шаги из первого примера.

{% Aside %} Как альтернативный способ решения проблемы, вы можете использовать кольцевую галерею на основе HTML-разметки, чтобы необходимые ресурсы подгружались при помощи [встроенного в браузер средства предварительной загрузки](https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/). Однако такой подход может не всегда быть практичным (например, при повторном использовании существующего компонента, который не основан на HTML-разметке). {% endAside %}

### Предварительная загрузка фоновых изображений при помощи image-set

Если на вашей странице в зависимости от плотности пикселей экрана используются различные фоновые изображения, вы можете указать их в CSS, используя синтаксис `image-set`. Тогда браузер сможет выбрать наиболее подходящее изображение исходя из [DPR](https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio) экрана.

```css
background-image: image-set( "cat.png" 1x, "cat-2x.png" 2x);
```

{% Aside %}

Приведенный выше синтаксис не учитывает тот факт, что для работы этой функции в браузерах на основе как Chromium, так и WebKit необходимо использовать префиксы поставщиков. Если вы планируете использовать эту функцию, рекомендуем вам инструмент [Autoprefixer](https://github.com/postcss/autoprefixer) для автоматического их добавления.

{% endAside %}

Недостаток фоновых изображений, указываемых при помощи CSS, заключается в том, что браузер обнаруживает их только после загрузки и обработки всего содержащегося в разделе `<head>` CSS-кода, которого может быть очень много.

Эту проблему можно изучить на примере сайта с [адаптивным фоновым изображением](https://responsive-preload.glitch.me/background_no_preload.html).

<figure>{% Img src="image/admin/7sjFt1RsoEOKn5zlS5zb.png", alt="Скриншот панели «Сеть» в Chrome DevTools.", width="800", height="451" %} <figcaption>В этом примере изображение начинает загружаться только после окончания загрузки CSS-кода, что приводит к нежелательной задержке перед его отображением.</figcaption></figure>

Предварительная загрузка адаптивных изображений — это простой и изящный способ ускорить загрузку таких изображений.

```html
<link rel=preload href=cat.png as=image imagesrcset="cat.png 1x, cat-2x.png 2x">
```

Вы можете изучить, как меняется поведение предыдущего примера при использовании [предварительной загрузки адаптивного фонового изображения](https://responsive-preload.glitch.me/background_preload.html).

<figure> {% Img src="image/admin/dOI6EmChfahBujnZOke7.png", alt="Скриншот панели «Сеть» в Chrome DevTools.", width="800", height="439" %} <figcaption>Как вы видите, загрузка изображения и CSS-кода начинается одновременно, что помогает избежать задержек и ускорить показ изображения.</figcaption></figure>

## Предварительная загрузка адаптивных изображений в действии

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

Чтобы ответить на этот вопрос, я создал две копии [демонстрационного PWA-приложения магазина](https://github.com/GoogleChromeLabs/sample-pie-shop); [одна из копий](https://20190710t144416-dot-pie-shop-app.appspot.com/apparel) не использует предварительную загрузку изображений, а [другая](https://20190710t132936-dot-pie-shop-app.appspot.com/apparel) использует ее для некоторых изображений. Поскольку на сайте применяется отложенная загрузка изображений при помощи JavaScript, его работу, вероятно, можно ускорить за счет предварительной загрузки изображений, расположенных в исходной области просмотра.

С результатами вы можете ознакомиться [здесь](https://www.webpagetest.org/result/190710_VM_30b9d4c993a1e60befba17e1261ba1ca/) (без предварительной загрузки) и [здесь](https://www.webpagetest.org/result/190710_7B_a99e792121760f81a270b4b9c847797b/) (с предзагрузкой изображений). Глядя на цифры, мы видим, что показатель [Start Render](https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/getting-started.md#start-render) («начало рендеринга») остался прежним, [Speed Index](https://developer.chrome.com/docs/lighthouse/performance/speed-index/) («индекс скорости») слегка улучшился (273 мс, так как изображения загружаются быстрее, но занимают небольшую площадь от общего числа пикселей), но сильнее всего отражает разницу показатель [Last Painted Hero](https://github.com/WPO-Foundation/webpagetest/blob/master/docs/Metrics/HeroElements.md) («последнее отрисованное hero-изображение»), который улучшился на 1,2 секунды. 🎉🎉

Конечно, ничто не передает разницу так наглядно, как покадровая визуализация:

<figure> {% Img src="image/admin/sXyZOvsNoAY0K2NRqT4U.png", alt="Скриншот покадрового сравнения WebPageTest, показывающего, что благодаря предзагрузке изображения показываются на 1,5 секунды быстрее.", width="800", height="328" %} <figcaption>На этом покадровом сравнении видно, что при использовании предварительной загрузки изображения появляются значительно раньше, что существенно повышает комфорт пользователя.</figcaption></figure>

## Предварительная загрузка и `<picture>`

Если вы знакомы с адаптивными изображениями, у вас может возникнуть вопрос: «А что насчет тега [`<picture>`](https://developer.mozilla.org/docs/Web/HTML/Element/picture)?».

Рабочая группа Web Performance Working Group рассматривает возможность расширить поддержку предварительной загрузки на атрибуты `srcset` и `sizes`, но не на элемент `<picture>`, который используется для так называемого [«художественного преобразования»](/codelab-art-direction/) .

Почему этим сценарием использования пренебрегают?

Хотя данный сценарий использования также представляет интерес, существует ряд [нерешенных технических проблем](https://calendar.perfplanet.com/2018/how-the-sausage-is-made-webperfwg-meeting-summary/), значительно усложняющих реализацию возможного решения. Кроме того, данный сценарий, по всей видимости, решается существующими средствами, хотя и не очень изящным способом (см. ниже).

На основании этого рабочая группа Web Performance решила сначала реализовать поддержку предзагрузки для атрибута `srcset`, а затем оценить спрос на аналогичную функциональность для элемента `picture`.

Если вам все-таки понадобится предварительная загрузка элемента `<picture>`, в качестве альтернативного решения вы можете использовать метод, описанный ниже.

Рассмотрим следующий пример:

```html
<picture>
    <source src="small_cat.jpg" media="(max-width: 400px)">
    <source src="medium_cat.jpg" media="(max-width: 800px)">
    <img src="huge_cat.jpg">
</picture>
```

Логика работы элемента `<picture>` (или, если точнее, логика выбора источника изображения) такова: элементы `<source>` обходятся по порядку, пока не будет найден первый элемент с подходящим значением атрибута `media`. Именно этот элемент используется в качестве источника ресурса.

Механизм адаптивной предварительной загрузки не учитывает порядок элементов, и критерии необходимо указывать с учетом этого факта:

```html
<link rel="preload" href="small_cat.jpg" as="image" media="(max-width: 400px)">
<link rel="preload" href="medium_cat.jpg" as="image" media="(min-width: 400.1px) and (max-width: 800px)">
<link rel="preload" href="large_cat.jpg" as="image" media="(min-width: 800.1px)">
```

## Подведение итогов

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