Адаптивные изображения 201: технология Client Hints
В прошлом году Джейсон написал серию статей Адаптивные изображения 101, посвященных новому синтаксису разметки, позволяющему управлять выдачей изображений. В продолжение этой темы необходимо рассказать о Client Hints.
Client Hints – довольно сложная вещь, ее было непросто реализовать. Но она способна значительно упростить дизайнерам ежедневный труд по написанию адаптивного кода.
Что такое Client Hints?
Client Hints – это протокол, при помощи которого браузер в заголовке запроса HTTP может сообщить серверу, какой тип контента он предпочитает получить.
Каждый запрос браузера серверу начинается с HTTP-заголовка. Например, заголовок запроса, который посылает Firefox, чтобы получить нашу домашнюю страницу, выглядит вот так:
GET / HTTP/1.1 Host: cloudfour.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:47.0) Gecko/20100101 Firefox/47.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: _ga=GA1.2.333160365.1465672418 Connection: keep-alive
Заголовки HTTP можно посмотреть с помощью инструментов разработчика любого браузера:
Рис. 1: Заголовки запросов для страницы Cloudfour.com в инструментах разработчика Chrome
Client Hints добавляют дополнительные поля в заголовки HTTP-запросов, содержащие информацию о браузере. Одно из главных применений этой возможности – информирование сервера, какого размера изображения нужны странице.
Client Hints и адаптивные изображения
В Client Hints есть три параметра для запроса соответствующих браузеру изображений:
- DPR – device pixel ratio, плотность пикселей, величина аналогичная разрешению
- Viewport-Width – размеры экрана
- Width – ширина изображения на странице
Из них наиболее полезны DPR и Width. Как уже рассказывалось в предыдущих статьях, размер viewport малополезен, на узких экранах смартфонов дизайнеры делают изображения почти в ширину экрана, а на десктопах – некоего определенного макетом страницы размера, но в итоге и там и там актуальная ширина изображения оказывается одной и той же.
Client Hints облегчают разметку
Давайте взглянем на пример кода предыдущих статей 101 серии:
<img src="cat.jpg" srcset="cat-160.jpg 160w, cat-320.jpg 320w, cat-640.jpg 640w, cat-1280.jpg 1280w" sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">
Смысл добавления атрибута srcset в разметку – создать для браузера набор изображений различного размера, чтобы он имел возможность запросить то, что ему необходимо. Сервер, в свою очередь, будет знать, какое именно изображение передать браузеру.
Это все обеспечивает свойство Width из Client Hints. Оно вычисляет необходимый размер на основе указанных в sizes размеров и сообщает его в заголовке запроса серверу:
GET cat.jpg Accept: image/webp,image/*,*/*;q=0.8 DPR: 2 Viewport-Width: 1024 Width: 508
Теперь же, если и клиент, и сервер поддерживают Clients Hints, разметка может быть сокращена до:
<img src="cat.jpg" sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">
Введение этого новшества снимает с дизайнеров и разработчиков всю тяжесть ответственности за выбор границ размеров изображений для тех или иных экранов и перекладывает ее на сервер.
Поддержка множественных форматов изображений
Помимо этого упрощения разметки, еще одно существенное сокращение может быть сделано, если совместить поле Accept-заголовка с возможностями Client Hints.Это поле используется браузером для того, чтобы сообщить серверу, какие типы данных он считает приемлемыми получить в ответ на данный запрос. Например, когда Chrome запрашивает изображение, он пишет примерно следующее:
Accept: image/webp,image/*,*/*;q=0.8
Здесь написано, что Chrome предпочитает форматы изображений Webp, но если таких нет, то он примет и другие, главное, чтобы их тип начинался с image. Ну, а уж если и такого нет, шлите то, что есть, тут на месте разберемся, говорит Chrome.Последняя запись q=0.8 говорит серверу, насколько сильно Chrome предпочитает именно такой формат изображений.Добавление в заголовок поля Accept позволяет сильно сократить разметку. Вот что было до того:
<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>
Здесь куча всего: и разные размеры изображений, и разные их форматы, масса источников изображений и т. п. Это, конечно, экстремальный пример. А вот что стало после упрощения с использованием поля Accept:
<picture> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing"> <img sizes="100vw" src="/image/thing-crop"> </picture>
Удивительно, правда? Обратите внимание, что пути к изображениям в упрощенной разметке не содержат расширений файлов. Это потому, что Accept говорит серверу, какие типы изображений клиент принимает и какие из них предпочтительны. И это позволяет серверу самому подобрать наилучшее изображение и послать его клиенту.
Забудьте scrset и picture, дайте мне Client Hints!
Некоторые, наверное, удивляются, зачем тратить столько времени на изучение синтаксиса srcset и picture, когда Client Hints делают их практически не нужными.
Не все браузеры поддерживают Client HintsНа сегодня только Chrome и Opera. На стадии рассмотрения – Microsoft Edge и Firefox
Для работоспособности страниц нужен серверИногда HTML используется оффлайн, например, для разметки электронных книг. Но вот разобрать запросы в заголовках и правильно их обработать может только сервер.
Сервер должен поддерживать Client Hints
Сервер должен знать, что делать с запросами Client Hints в заголовках. Мы со временем увидим, как Apache и Nginx добавили Client Hints, но для этого потребуется время, поскольку тут нужно не только разобрать запросы, но нужна еще и система обработки самих изображений.
В краткосрочной перспективе использование Client Hint может заключаться в подключении службы масштабирования изображений, которая будет использовать эту спецификацию. Cloudinary, Imgix и Scientiamobile уже поддерживают Client Hints.
Поддержка Client Hints на сервере – непростая задача
Как и многое другое, связанное с изображениями, реализация Client Hints на сервере сначала кажется легкой. Браузер сообщает серверу размер изображения на странице. Сервер возвращает изображение правильного размера. Всё.
Но начни это делать, и тут же возникают различные проблемы:
Для обеспечения всех возможных клиентов масштабируемыми ресурсами, мы должны обработать и хранить сотни, а то и тысячи копий изображений. Это весьма затратно в смысле вычислительных ресурсов и, кроме того, затрудняет функционирование страниц. Разнообразие ресурсов означает меньшую эффективность использования кэша. Это еще больше замедляет работу, поскольку требуется время на обработку промаха — поиск не найденного в кэше ресурса и его последующее кэширование.
Также, возможно, и не нужны слишком точные настройки, связанные с небольшими изменениями размера экрана или с изменениями его ориентации – может быть, подгрузка чуть большего, чем надо изображения не так уж и вредит, во всяком случае, большее изображение, будучи отмасштабировано браузером, не теряет в качестве.И нам в самом деле не нужно, хотя и хочется, кэшировать весь массив вариантов изображений в CDN – это будет стоить довольно дорого.
В итоге разные службы обработки изображений могут дать разный результат. Мы дадим им Client Hints – но вот насколько разумно и оптимально они будут обрабатывать эти запросы?
Client Hints нужно подключать опционально
Поскольку разработчикам браузеров не очень интересно посылать лишнюю информацию в каждом запросе, нам нужно явным образом включить Client Hints в теге meta:
<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
Надо просто указать список спецификаций Client Hints, которые мы будем использовать, чтобы избежать лишних записей в заголовках запросов.
Client Hints – и курица и яйцоПодключение по тегу meta создает головоломку: когда браузер получает страницу, он еще не знает, включен ли Client Hints тегом meta. И сервер не знает, поддерживает ли браузер Client Hints, поскольку пока тег meta не прочитан, браузер даст серверу соответствующие запросы. Это закрывает возможности серверу оптимизировать разметку высылаемого по первому запросу контента и лишает возможности разработчиков оптимизировать код.
Но опциональность – это принципиальный момент, это способ согласовать различные подходы к организации пересылаемого контента вообще. Многие производители браузеров по вполне достойным уважения причинам выступают против какой-либо подгонки, согласования передаваемого контента. А Client Hints – как раз способ такого согласования. Поэтому его нельзя включать по умолчанию.
Как же решить эту головоломку с опциональностью?Если сервер не знает, поддерживаются ли Client Hints до создания разметки, как мы узнаем, какую именно разметку разрабатывать? Допустим, мы хотим использовать Client Hints. Тут возможны два решения:
- Определять агента на стороне сервера
- Поддерживать одновременно два типа разметки: под Client Hints и под адаптивные изображения
Я утверждаю, что если вы используете и разметку для адаптивных изображений и Client Hints, включив его в теге meta, то тогда браузер будет разбирать разметку для адаптивных изображений и запрашивать необходимое в заголовках HTTP.
Так что в краткосрочной перспективе правильнее поддерживать оба вида разметки. Но на самом деле, реально все будет определяться возможностями службы обработки изображений вашего провайдера. Потому сначала проконсультируйтесь у них, что они могут предложить.
Нам все еще нужен синтаксис разметки для работы с адаптивными изображениями
Client Hints – прорывная технология и за ней большое будущее. Когда она будет поддерживаться повсеместно, все, что нам будет нужно из синтаксиса адаптивных изображений – только тэг sizes. Вот почему тэг sizes может стать супергероем адаптивных изображений.
Client Hints также говорят нам, что обработка изображений, их классификация по размерам не должна делаться людьми.