Разработка для Интернета дает вам беспрецедентный охват. Ваше веб-приложение находится на расстоянии одного клика и доступно практически на каждом подключенном устройстве: смартфоне, планшете, ноутбуке и настольном компьютере, телевизоре и т. д., независимо от бренда или платформы. Чтобы обеспечить максимальное удобство работы, вы создали адаптивный сайт , который адаптирует представление и функциональность для каждого форм-фактора, и теперь вы выполняете контрольный список производительности, чтобы гарантировать, что приложение загружается как можно быстрее: вы оптимизировали свой критический путь рендеринга , вы сжали и кэшировали свои текстовые ресурсы и теперь смотрите на свои графические ресурсы, на которые часто приходится большая часть передаваемых байтов. Проблема в том, что оптимизация изображений сложна :
- Определите подходящий формат (векторный или растровый)
- Определить оптимальные форматы кодирования (jpeg, webp и т. д.)
- Определите правильные настройки сжатия (с потерями или без потерь)
- Определите, какие метаданные следует сохранить или удалить.
- Сделайте несколько вариантов каждого для каждого дисплея + разрешение DPR.
- ...
- Учитывайте тип сети, скорость и предпочтения пользователя.
По отдельности это хорошо понятные проблемы . В совокупности они создают большое пространство для оптимизации, которое мы (разработчики) часто упускаем из виду или пренебрегаем. Люди плохо справляются с повторным исследованием одного и того же пространства поиска, особенно когда требуется много шагов. С другой стороны, компьютеры превосходно справляются с задачами такого типа.
Ответ на хорошую и устойчивую стратегию оптимизации изображений и других ресурсов с похожими свойствами прост: автоматизация. Если вы настраиваете свои ресурсы вручную, вы делаете это неправильно: вы забудете, станете ленивы или кто-то другой сделает эту ошибку за вас — гарантировано.
Сага о разработчике, заботящемся о производительности
Поиск в пространстве оптимизации изображений состоит из двух отдельных этапов: времени сборки и времени выполнения.
- Некоторые оптимизации присущи самому ресурсу — например, выбор подходящего формата и типа кодирования, настройка параметров сжатия для каждого кодировщика, удаление ненужных метаданных и т. д. Эти шаги можно выполнить во время сборки.
- Другие оптимизации определяются типом и свойствами запрашивающего ее клиента и должны выполняться «во время выполнения»: выбор соответствующего ресурса для DPR клиента и предполагаемой ширины дисплея, учет скорости сети клиента, предпочтений пользователя и приложения и т. д. на.
Инструменты для сборки существуют, но их можно улучшить. Например, можно добиться значительной экономии за счет динамической настройки параметра «качество» для каждого изображения и каждого формата изображения, но я еще не видел, чтобы кто-нибудь действительно использовал это за пределами исследований. Это область, созревшая для инноваций, но для целей данной статьи я оставлю все как есть. Давайте сосредоточимся на оперативной части истории.
<img src="/image/thing" sizes="50vw"
alt="image thing displayed at 50% of viewport width">
Цель приложения очень проста: получить и отобразить изображение на 50% области просмотра пользователя. Именно здесь почти каждый дизайнер моет руки и направляется в бар. Тем временем разработчика, заботящегося о производительности, ждет долгая ночь:
- Чтобы получить наилучшее сжатие, она хочет использовать оптимальный формат изображения для каждого клиента: WebP для Chrome, JPEG XR для Edge и JPEG для остальных.
- Чтобы добиться наилучшего визуального качества, ей необходимо создать несколько вариантов каждого изображения с разным разрешением: 1x, 1,5x, 2x, 2,5x, 3x и, возможно, даже несколько промежуточных.
- Чтобы избежать появления ненужных пикселей, ей необходимо понимать, что на самом деле означает «50% области просмотра пользователя» — существует множество различных значений ширины области просмотра!
- В идеале она также хочет обеспечить отказоустойчивость , при которой пользователи в более медленных сетях будут автоматически выбирать более низкое разрешение. В конце концов, пришло время стеклить.
- Приложение также предоставляет некоторые пользовательские элементы управления, которые влияют на то, какой ресурс изображения должен быть получен, так что это тоже следует учитывать.
Да, и тогда дизайнер понимает, что ей нужно отображать другое изображение с шириной 100%, если размер области просмотра мал, чтобы оптимизировать читаемость. Это означает, что теперь нам нужно повторить тот же процесс для еще одного ресурса, а затем сделать выборку зависящей от размера области просмотра. Я уже говорил, что это сложно? Ну ок, давайте к делу. Элемент picture
поможет нам довольно далеко :
<picture>
<!-- serve WebP to Chrome and Opera -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
/image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
/image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
type="image/webp">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
/image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
/image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
type="image/webp">
<!-- serve JPEGXR to Edge -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
/image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
/image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
type="image/vnd.ms-photo">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
/image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
/image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
type="image/vnd.ms-photo">
<!-- serve JPEG to others -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
/image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
/image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
/image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
/image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
<!-- fallback for browsers that don't support picture -->
<img src="/image/thing.jpg" width="50%">
</picture>
Мы позаботились о художественном оформлении, выборе формата и предоставили шесть вариантов каждого изображения, чтобы учесть различия в DPR и ширине области просмотра устройства клиента. Впечатляющий!
К сожалению, элемент picture
не позволяет нам определять какие-либо правила того, как он должен вести себя в зависимости от типа или скорости соединения клиента. Тем не менее, его алгоритм обработки в некоторых случаях позволяет пользовательскому агенту регулировать, какой ресурс он извлекает — см. шаг 5. Нам просто остается надеяться, что пользовательский агент достаточно умен. (Примечание: ни одна из текущих реализаций не является таковой). Аналогично, в элементе picture
нет перехватчиков, позволяющих использовать логику, специфичную для приложения, которая учитывает предпочтения приложения или пользователя. Чтобы получить эти последние два бита, нам пришлось бы переместить всю вышеописанную логику в JavaScript, но при этом теряется оптимизация сканера предварительной загрузки, предлагаемая picture
. Хм.
Если оставить в стороне эти ограничения, это работает. Ну, по крайней мере, для этого конкретного актива. Реальная и долгосрочная проблема заключается в том, что мы не можем ожидать, что дизайнер или разработчик будет вручную создавать такой код для каждого ресурса. С первой попытки это забавная головоломка, но сразу после этого она теряет свою привлекательность. Нам нужна автоматизация. Возможно, IDE или другие инструменты преобразования контента могут спасти нас и автоматически сгенерировать приведенный выше шаблон.
Автоматизация выбора ресурса с помощью подсказок клиенту
Сделайте глубокий вдох, отбросьте свое неверие и теперь рассмотрите следующий пример:
<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
<source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
<img sizes="100vw" src="/image/thing-crop">
</picture>
Хотите верьте, хотите нет, но приведенного выше примера достаточно, чтобы предоставить все те же возможности, что и гораздо более длинная разметка изображения выше, плюс, как мы увидим, он дает разработчику полный контроль над тем, как, какие и когда извлекаются ресурсы изображения. «Магия» находится в первой строке, которая позволяет сообщать о подсказках клиента и сообщает браузеру рекламировать соотношение пикселей устройства ( DPR
), ширину области просмотра макета ( Viewport-Width
) и предполагаемую ширину отображения ( Width
) ресурсов для сервер.
Если включены клиентские подсказки, результирующая разметка на стороне клиента сохраняет только требования к представлению . Дизайнеру не нужно беспокоиться о типах изображений, разрешениях клиента, оптимальных точках останова для уменьшения количества доставленных байтов или других критериях выбора ресурсов. Давайте посмотрим правде в глаза: они никогда этого не делали, и им не следовало этого делать. Более того, разработчику также не нужно переписывать и расширять приведенную выше разметку, поскольку фактический выбор ресурсов согласовывается клиентом и сервером.
Chrome 46 обеспечивает встроенную поддержку подсказок DPR
, Width
и Viewport-Width
. По умолчанию подсказки отключены, а приведенный выше <meta http-equiv="Accept-CH" content="...">
служит сигналом согласия, который сообщает Chrome о необходимости добавлять указанные заголовки к исходящим запросам. Имея это в виду, давайте рассмотрим заголовки запроса и ответа для примера запроса изображения:
Chrome объявляет о своей поддержке формата WebP через заголовок запроса Accept ; новый браузер Edge аналогичным образом рекламирует поддержку JPEG XR через заголовок Accept.
Следующие три заголовка запроса — это заголовки client-hint, сообщающие о соотношении пикселей устройства клиента (3x), ширине области просмотра макета (460 пикселей) и предполагаемой ширине отображения ресурса (230 пикселей). Это предоставляет серверу всю необходимую информацию для выбора оптимального варианта изображения на основе его собственного набора политик: наличие предварительно сгенерированных ресурсов, стоимость перекодирования или изменения размера ресурса, популярность ресурса, текущая загрузка сервера и т. д. . В этом конкретном случае сервер использует подсказки DPR
и Width
и возвращает ресурс WebP, как указано в заголовках Content-Type
, Content-DPR
и Vary
.
Здесь нет никакой магии. Мы перенесли выбор ресурсов из HTML-разметки в процесс согласования запроса и ответа между клиентом и сервером. В результате HTML касается только требований к представлению, и мы можем доверить его написание любому дизайнеру и разработчику, в то время как поиск в пространстве оптимизации изображений переносится на компьютеры и теперь легко автоматизируется в масштабе. Помните нашего разработчика, заботящегося о производительности? Теперь ее работа — написать сервис изображений, который сможет использовать предоставленные подсказки и возвращать соответствующий ответ: она может использовать любой язык или сервер, который ей нравится, или позволить стороннему сервису или CDN сделать это от ее имени.
<img src="/image/thing" sizes="50vw"
alt="image thing displayed at 50% of viewport width">
Кроме того, помните этого парня выше? Благодаря подсказкам клиента скромный тег изображения теперь учитывает DPR, область просмотра и ширину без какой-либо дополнительной разметки. Если вам нужно добавить художественное оформление, вы можете использовать тег picture
, как мы показали выше, в противном случае все ваши существующие теги изображений станут намного умнее. Клиентские подсказки улучшают существующие элементы img
и picture
.
Получение контроля над выбором ресурсов с помощью сервис-воркера
ServiceWorker, по сути, представляет собой прокси-сервер на стороне клиента, работающий в вашем браузере. Он перехватывает все исходящие запросы и позволяет проверять, перезаписывать, кэшировать и даже синтезировать ответы. Изображения ничем не отличаются, и при включенных клиентских подсказках активный ServiceWorker может идентифицировать запросы изображений, проверять предоставленные клиентские подсказки и определять свою собственную логику обработки.
self.onfetch = function(event) {
var req = event.request.clone();
console.log("SW received request for: " + req.url)
for (var entry of req.headers.entries()) {
console.log("\t" + entry[0] +": " + entry[1])
}
...
}
ServiceWorker предоставляет вам полный контроль над выбором ресурсов на стороне клиента . Это очень важно. Позвольте этому осознаться, потому что возможности почти безграничны:
- Вы можете переписать значения заголовка подсказок клиента, установленные пользовательским агентом.
- Вы можете добавить к запросу новые значения заголовков подсказок клиента.
- Вы можете переписать URL-адрес и указать запрос изображения на альтернативном сервере (например, CDN).
- Вы даже можете переместить значения подсказок из заголовков в сам URL-адрес, если это облегчит развертывание в вашей инфраструктуре.
- Вы можете кэшировать ответы и определять собственную логику, по которой обслуживаются ресурсы.
- Вы можете адаптировать свой ответ в зависимости от возможностей подключения пользователей.
- Вы можете использовать API NetInfo для запроса и объявления своих предпочтений на сервере.
- Вы можете вернуть альтернативный ответ, если выборка выполняется медленно.
- Вы можете учитывать переопределение предпочтений приложений и пользователей.
- Вы можете… делать все, что душе угодно, правда.
Элемент picture
обеспечивает необходимое управление художественным направлением в разметке HTML. Подсказки клиента предоставляют аннотации к результирующим запросам изображений, которые позволяют автоматизировать выбор ресурсов. ServiceWorker предоставляет возможности управления запросами и ответами на клиенте. Это расширяемая сеть в действии.
Клиент подсказывает FAQ
Где доступны подсказки для клиентов? Поставляется в Chrome 46 . На рассмотрении в Firefox и Edge .
Почему клиентские подсказки включены? Мы хотим минимизировать накладные расходы для сайтов, которые не будут использовать клиентские подсказки. Чтобы включить подсказки клиента, сайт должен предоставить заголовок
Accept-CH
или эквивалентную директиву<meta http-equiv>
в разметке страницы. При наличии любого из них пользовательский агент будет добавлять соответствующие подсказки ко всем запросам подресурсов. В будущем мы можем предоставить дополнительный механизм для сохранения этого предпочтения для определенного источника, что позволит доставлять те же подсказки по навигационным запросам.Зачем нам нужны клиентские подсказки, если у нас есть ServiceWorker? ServiceWorker не имеет доступа к информации о макете, ресурсах и ширине области просмотра. По крайней мере, не без введения дорогостоящих двусторонних обходов и значительной задержки запроса изображения - например, когда запрос изображения инициируется анализатором предварительной загрузки. Клиентские подсказки интегрируются с браузером, чтобы сделать эти данные доступными как часть запроса.
Клиентские подсказки предназначены только для ресурсов изображений? Основной вариант использования подсказок DPR, Viewport-Width и Width — включить выбор ресурсов для графических ресурсов. Однако одни и те же подсказки предоставляются для всех подресурсов независимо от типа — например, запросы CSS и JavaScript также получают одну и ту же информацию и также могут использоваться для оптимизации этих ресурсов.
Некоторые запросы изображений не сообщают ширину, почему? Браузер может не знать предполагаемую ширину отображения, поскольку сайт полагается на внутренний размер изображения. В результате подсказка ширины опускается для таких запросов, а также для запросов, которые не имеют «ширины отображения» — например, ресурса JavaScript. Чтобы получать подсказки по ширине, обязательно укажите значение размеров на изображениях.
А как насчет <вставьте мою любимую подсказку> ? ServiceWorker позволяет разработчикам перехватывать и изменять (например, добавлять новые заголовки) все исходящие запросы. Например, легко добавить информацию на основе NetInfo для указания текущего типа соединения — см. « Отчеты о возможностях с помощью ServiceWorker ». «Встроенные» подсказки, поставляемые в Chrome (DPR, Width, Resource-Width), реализованы в браузере, поскольку реализация на основе чистого программного обеспечения будет задерживать все запросы изображений.
Где я могу узнать больше, посмотреть больше демонстраций и что? Ознакомьтесь с поясняющим документом и смело открывайте проблему на GitHub, если у вас есть отзывы или другие вопросы.