Автоматизация выбора ресурса с помощью подсказок клиенту

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

  • Определите подходящий формат (векторный или растровый)
  • Определить оптимальные форматы кодирования (jpeg, webp и т. д.)
  • Определите правильные настройки сжатия (с потерями или без потерь)
  • Определите, какие метаданные следует сохранить или удалить.
  • Сделайте несколько вариантов каждого для каждого дисплея + разрешение DPR.
  • ...
  • Учитывайте тип сети, скорость и предпочтения пользователя.

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

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

Сага о разработчике, заботящемся о производительности

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

  • Некоторые оптимизации присущи самому ресурсу — например, выбор подходящего формата и типа кодирования, настройка параметров сжатия для каждого кодировщика, удаление ненужных метаданных и т. д. Эти шаги можно выполнить во время сборки.
  • Другие оптимизации определяются типом и свойствами запрашивающего ее клиента и должны выполняться «во время выполнения»: выбор соответствующего ресурса для DPR клиента и предполагаемой ширины дисплея, учет скорости сети клиента, предпочтений пользователя и приложения и т. д. на.

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

<img src="/image/thing" sizes="50vw"
        alt="image thing displayed at 50% of viewport width">

Цель приложения очень проста: получить и отобразить изображение на 50% области просмотра пользователя. Именно здесь почти каждый дизайнер моет руки и направляется в бар. Тем временем разработчика, заботящегося о производительности, ждет долгая ночь:

  1. Чтобы получить наилучшее сжатие, она хочет использовать оптимальный формат изображения для каждого клиента: WebP для Chrome, JPEG XR для Edge и JPEG для остальных.
  2. Чтобы добиться наилучшего визуального качества, ей необходимо создать несколько вариантов каждого изображения с разным разрешением: 1x, 1,5x, 2x, 2,5x, 3x и, возможно, даже несколько промежуточных.
  3. Чтобы избежать добавления ненужных пикселей, ей необходимо понять, что на самом деле означает «50% области просмотра пользователя» — существует множество различных значений ширины области просмотра!
  4. В идеале она также хочет обеспечить отказоустойчивость , при которой пользователи в более медленных сетях будут автоматически выбирать более низкое разрешение. В конце концов, пришло время стеклить.
  5. Приложение также предоставляет некоторые пользовательские элементы управления, которые влияют на то, какой ресурс изображения должен быть получен, так что это тоже следует учитывать.

Да, и тогда дизайнер понимает, что ей нужно отображать другое изображение с шириной 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.

ServiceWorker предоставляет вам полный контроль над выбором ресурсов на стороне клиента . Это очень важно. Позвольте этому осознаться, потому что возможности почти безграничны:

  • Вы можете переписать значения заголовка подсказок клиента, установленные пользовательским агентом.
  • Вы можете добавить к запросу новые значения заголовков подсказок клиента.
  • Вы можете переписать URL-адрес и указать запрос изображения на альтернативном сервере (например, CDN).
    • Вы даже можете переместить значения подсказок из заголовков в сам URL-адрес, если это облегчит развертывание в вашей инфраструктуре.
  • Вы можете кэшировать ответы и определять собственную логику, по которой обслуживаются ресурсы.
  • Вы можете адаптировать свой ответ в зависимости от возможностей подключения пользователей.
  • Вы можете учитывать переопределение предпочтений приложений и пользователей.
  • Вы можете… делать все, что душе угодно, правда.

Элемент picture обеспечивает необходимое управление художественным направлением в разметке HTML. Подсказки клиента предоставляют аннотации к результирующим запросам изображений, которые позволяют автоматизировать выбор ресурсов. ServiceWorker предоставляет возможности управления запросами и ответами на клиенте. Это расширяемая сеть в действии.

Клиент подсказывает FAQ

  1. Где доступны подсказки для клиентов? Поставляется в Chrome 46 . На рассмотрении в Firefox и Edge .

  2. Почему клиентские подсказки включены? Мы хотим минимизировать накладные расходы для сайтов, которые не будут использовать клиентские подсказки. Чтобы включить подсказки для клиента, сайт должен предоставить заголовок Accept-CH или эквивалентную директиву <meta http-equiv> в разметке страницы. При наличии любого из них пользовательский агент будет добавлять соответствующие подсказки ко всем запросам подресурсов. В будущем мы можем предоставить дополнительный механизм для сохранения этого предпочтения для определенного источника, что позволит доставлять те же подсказки по навигационным запросам.

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

  4. Клиентские подсказки предназначены только для ресурсов изображений? Основной вариант использования подсказок DPR, Viewport-Width и Width — включить выбор ресурсов для графических ресурсов. Однако одни и те же подсказки предоставляются для всех подресурсов независимо от типа — например, запросы CSS и JavaScript также получают одну и ту же информацию и также могут использоваться для оптимизации этих ресурсов.

  5. Некоторые запросы изображений не сообщают ширину, почему? Браузер может не знать предполагаемую ширину отображения, поскольку сайт полагается на внутренний размер изображения. В результате подсказка ширины опускается для таких запросов, а также для запросов, которые не имеют «ширины отображения» — например, ресурса JavaScript. Чтобы получать подсказки по ширине, обязательно укажите значение размеров на изображениях.

  6. А как насчет <вставьте мою любимую подсказку> ? ServiceWorker позволяет разработчикам перехватывать и изменять (например, добавлять новые заголовки) все исходящие запросы. Например, легко добавить информацию на основе NetInfo для указания текущего типа соединения — см. « Отчеты о возможностях с помощью ServiceWorker ». «Встроенные» подсказки, поставляемые в Chrome (DPR, Width, Resource-Width), реализованы в браузере, поскольку реализация на основе чистого ПО будет задерживать все запросы изображений.

  7. Где я могу узнать больше, посмотреть больше демонстраций и что? Ознакомьтесь с поясняющим документом и смело открывайте проблему на GitHub, если у вас есть отзывы или другие вопросы.