[Перевод] HTTP-заголовок Feature-Policy и контроль поведения веб-страниц в браузерах
Существует одна совершенно бесподобная методика, позволяющая держать производительность веб-проекта под контролем. Она заключается во внедрении в процесс разработки механизмов, результаты работы которых хорошо заметны. Эти механизмы нацелены на то, чтобы всегда напоминать программисту о важности производительности. В этом контексте есть кое-что, что мне очень нравится. Это — HTTP-заголовок Feature-Policy.
Этот заголовок — сравнительно новая возможность, которая позволяет разработчику сделать так, чтобы во время просмотра его сайта включались и отключались некоторые возможности браузера.
Например, можно сообщить браузеру о том, что он не должен позволять использовать API Geolocation, передав ему следующий заголовок:
Feature-Policy: geolocation 'none'
У использования заголовка Feature-Policy
есть, с точки зрения безопасности и производительности, множество плюсов. Но мне сейчас особенно нравится то, как Feature-Policy
можно использовать для того, чтобы сделать более заметными проблемы производительности сайтов, которые обычно легко проглядеть. Это можно сравнить с чем-то вроде «линтинга производительности». В частности, речь идёт о выявлении проблем с изображениями, используемыми в веб-проектах.
Политика oversized-images
Если браузеру передают изображение в формате, который он поддерживает, такое изображение будет выведено. Это — стандартное поведение браузера. Браузер, стремясь помочь тем, кто просматривает страницу, ещё и автоматически масштабирует подобные изображения. Поэтому они выглядят хорошо даже в том случае, если представлены огромными графическими файлами. В результате, если используются изображения, размеры которых больше размеров, нужных странице, то это, на первый взгляд, незаметно.
Политика (директива) oversized-images
сообщает браузеру о том, что он не должен позволять использование изображений, размер которых превышает, в заданное количество раз, размер их контейнеров. Изображение, в соответствии со стандартным ограничением, не может быть более чем в 2 раза больше контейнера. Но это значение, если надо, можно переопределить.
Итак, если браузер получит следующий заголовок, он не позволит показывать изображения, взятые из любых источников (это задаётся благодаря none
), размеры которых более чем в 2 раза превышают размеры их контейнеров (по ширине или по высоте).
Feature-Policy: oversized-images 'none';
Если нужно больше гибкости — можно сообщить браузеру о том, что он не должен выводить изображения, размеры которых более чем в 3 раза превышают размеры контейнеров:
Feature-Policy: oversized-images *(3) 'none';
В любом случае, если изображение не укладывается в заданные границы, вместо этого изображения будет выведена заглушка, а в консоль попадёт сообщение об ошибке.
Если на сайте используется политика oversized-images, то большие изображения будут загружаться, но вместо них будет отображаться заглушка, а в консоль будет выводиться сообщение об ошибке
Неоптимизированные изображения
Ещё одна распространённая проблема, связанная с графикой, заключается в использовании неоптимизированных изображений. Весьма часто можно столкнуться с изображениями, которые не были адекватным образом сжаты. Их размер, при этом, может быть подобран правильно. Так, к файлам с фотографиями, при их съёмке и создании, может быть добавлено много ненужных метаданных, которые часто остаются в файлах и при использовании их в браузерах. Один из особо раздражающих примеров подобного — это изображения, в метаданных которых содержатся их собственные миниатюры, предназначенные для предварительного просмотра. Много раз я видел, как встроенная в изображение миниатюра (о наличии которой дизайнеры и разработчики даже не знали), «весила» больше, чем само изображение!
Кроме всего прочего, тут можно вспомнить и обычное сжатие изображений, поддерживаемое многими форматами, которое позволяет достичь идеального баланса между качеством картинки и размером файла.
Политики unoptimized-lossy-images и unoptimized-lossless-images позволяют сообщить браузеру о том, чтобы он сопоставлял бы размер файла и размер изображения в пикселях.
Feature-Policy: unoptimized-lossy-images 'none';
Feature-Policy: unoptimized-lossless-images 'none';
Если показатель количества байт на пиксель (Byte-per-pixel, BPP) слишком высок, то браузер покажет вместо изображения заглушку и выведет в консоль сообщение об ошибке.
Применение политик unoptimized-* приводит к тому, что вместо неподходящих изображений выводится заглушка — так же, как и при использовании политики oversized-images
Рекомендованный уровень BPP для изображений, сжатых с потерями, составляет 0.5, а для изображений, сжатых без потерь — 1. Кроме того, при анализе изображений допускается небольшое отклонение их общего размера от этих уровней. Сейчас это отклонение для изображений, сжатых с потерями, составляет 1 Кб, для изображений, сжатых без потерь — 10 Кб.
Например, предположим, что у нас имеется JPEG-изображение размером 200×200 пикселей. JPEG — это формат сжатия изображений с потерями, в результате рекомендованный для него уровень BPP составляет 0.5. При этом общий размер изображения может превышать рекомендованный размер всего на 1 Кб. Для выяснения того, какой размер должно иметь подобное изображение, устраивающее браузер, нужно умножить размеры сторон изображения в пикселях друг на друга и на BPP, после чего — прибавить к тому, что получится, разрешённое отклонение.
(200 x 200 x .5) + 1024 = 21,024
байт, или 20.5
Кб
Если изображение сжато без потерь, то допускается отклонение от «идеального» размера, равное 10 Кб, при этом показатель BPP такого изображения будет равняться 1. В остальном вычисления выглядят точно так же:
(200 x 200 x 1) + 10,240 = 50,240
байт, или 49.1
Кб
Размер разрешённого отклонения, вероятно, в будущем изменится. На самом деле, хотя Blink, по умолчанию, использует этот показатель для изображений, сжатых без потерь, равняющийся 10 Кб, уже ведутся эксперименты с политикой unoptimized-lossless-images-strict
, которая меняет этот показатель, понижая его до уровня 1 Кб.
Медиа-материалы, размеры которых не указаны
Новое — это хорошо забытое старое.
Долгое время использование атрибутов изображений height
и width
было более или менее распространено. Без этих атрибутов браузер не знал о том, какое пространство должно занимать изображение, до того момента, когда изображение будет загружено. Это вело к сдвигам в макетах страниц. Страница выводилась, а потом, после прибытия изображения, её содержимое сдвигалось. Браузеру приходилось лишний раз пересчитывать макет для того чтобы выделить место для изображения.
Когда пришла пора макетов, в которых размеры изображений должны были гибко меняться с помощью CSS, наличие или отсутствие этих атрибутов уже не играло особенной роли. В результате многие просто перестали использовать эти атрибуты.
Но благодаря недавней работе, инициированной Джен Симмонс, Firefox и Chrome могут вычислять соотношение сторон изображений, основываясь на их атрибутах height
и width
. Когда данный подход объединяется с применяемым к изображениям стилям, оказывается, что браузеры могут резервировать место для изображений в ходе начального прохода формирования макета.
Политика unsized-media указывает браузеру на то, что все медиа-элементы должны иметь атрибуты, задающие размеры этих элементов. Если таких атрибутов нет — браузер должен использовать значения, принятые по умолчанию. Всё, на самом деле, немного сложнее, но суть тут в том, что если у изображения не заданы соответствующие атрибуты, браузер использует стандартное значение 300x150
пикселей.
Feature-Policy: unsized-media 'none';
Когда применяется эта политика, изображения будут выводиться, но если их размер в HTML не задан, разработчик быстро заметит, что изображения выводятся в размере, заданном по умолчанию. И, как обычно, в консоль попадёт сообщение об ошибке.
Применение политики unsized-media приводит к тому, что изображения и видео без атрибутов height и width выводятся, но браузер устанавливает их размеры как 300×150 пикселей
Вероятно, тут стоит сказать об одной особенности, которая поначалу показалась мне необычной. Она показывает себя при совместном использовании политик unsized-media
и oversized-images
. В такой ситуации не стоит удивляться появлению большего, чем раньше, количества сообщений о слишком больших изображениях. Дело в том, что из-за применения политики unsized-media
браузер изменяет размер изображений, которые не имеют атрибутов height
и width
, до 300x150
пикселей. После чего именно этот размер используется браузером в качестве точки отсчёта для определения соответствия изображения его контейнеру.
Привлечение внимания к менее заметным проблемам
В политиках, связанных с изображениями, мне нравится то, что они, в процессе разработки проектов, позволяют сделать заметным то, что обычно скрыто. В результате разработчик узнаёт о том, что он не позаботился об оптимизации изображений, или о том, что забыл задать их атрибуты height
и width
. Все эти ошибки тут же отражаются на внешнем виде страниц. На самом деле, главный плюс использования вышеописанных политик заключается в том, что они позволяют разработчику быстро узнавать о проблемах с изображениями. В то время как политика unsized-media
способна привести к уменьшению количества ситуаций, в которых происходят «сдвиги» макетов страниц, применение других политик не препятствует загрузке неподходящих изображений. В результате единственная сильная сторона применения этих политик заключается в том, что они привлекают внимание разработчиков к проблемам.
Есть ещё несколько политик, которые могут оказаться полезными с точки зрения анализа страниц на предмет влияния чего-либо на их производительность. Тут на ум приходят политики вроде sync-script (эта политика блокирует выполнение синхронных скриптов), sync-xhr
(блокирует синхронные AJAX-запросы) и document-write
(блокирует вызовы document.write
).
Эти политики — отличные инструменты, позволяющие контролировать определённые аспекты производительности страниц, но сами по себе они не дают разработчикам столь же заметной обратной связи, которую, в форме отсутствующих изображений, дают многие вышеописанные политики. Конечно, если на странице имеется синхронный скрипт, без которого она не выводится, то подобное нелегко не заметить (и подобные страницы не так уж и сложно найти). Но эти политики обычно указывают на ошибки в виде сообщений, выводимых в консоль. А я, если честно, подозреваю, что большинство разработчиков не уделяет консоли особенно много внимания (полагаю, нам всем стоит внимательнее относиться к консоли).
Но мы, тем не менее, можем сделать ошибки, выявляемые с помощью «невидимых» политик, гораздо более заметными, воспользовавшись API ReportingObserver для организации наблюдения за нарушениями и для вывода соответствующих уведомлений на странице. Такие уведомления можно сделать весьма заметными.
let reportingAlerts = document.createElement('ul');
reportingAlerts.setAttribute('id','reportingAlerts');
document.body.appendChild(reportingAlerts);
const alertBox = document.getElementById('reportingAlerts');
new ReportingObserver((reports, observer) => {
let fragment = document.createDocumentFragment();
Object.keys(reports).forEach(function(item) {
let li = document.createElement('li');
li.textContent = reports[item].body.message + ': ' + reports[item].body.featureId;
fragment.appendChild(li);
});
alertBox.appendChild(fragment)
}, {types: ['feature-policy-violation'], buffered: true}).observe();
Я набросал в CodePen пример того, как это может выглядеть.
Пример того, как можно выводить уведомления о нарушениях политик, задаваемых заголовком Feature-Policy. Подобные уведомления рассчитаны на вывод в окружении разработки, или там, где проект готовят к выводу в продакшн.
Минус использования заголовка Feature-Policy
Большой минус использования заголовка Feature-Policy
заключается в его поддержке браузерами. По всей видимости, сейчас его поддерживают лишь браузеры, основанные на Blink (Opera, Edge, Chrome, Samsung). Браузеры Firefox и Safari поддерживают атрибут allow
, предназначенный для элементов iframe
. Но даже там, где Feature-Policy
поддерживается, для обеспечения работоспособности многих из политик нужно включать флаг Experimental Web Platform features
(найти его можно по адресу about:flags
).
Как я пользуюсь заголовком Feature-Policy
Тот минус заголовка Feature-Policy
, о котором сказано выше, лично для меня особой проблемы не представляет. Я предпочитаю использовать политики, задаваемые этим заголовком, как браузерное средство проверки страниц. Поэтому мне не нужно стремиться к применению Feature-Policy
в продакшне, или к тому, чтобы политики работали бы у всех моих пользователей. Они нужны только мне, а так же тем, кто занимается разработкой сайта. Я, в любом случае, в качестве основного браузера, использую в процессе разработки Chrome. Поэтому обеспечение работоспособности Feature-Police
в моём рабочем окружении заключается во включении соответствующего флага. После этого политики работают без дополнительного вмешательства с моей стороны.
Я выяснил, что проще всего пользоваться заголовком Feature-Policy
с помощью браузерного расширения ModHeader. Это расширение позволяет задавать собственные заголовки, передаваемые страницам при их просмотре.
Расширение ModHeader позволяет настраивать варианты заголовка Feature-Policy. Их можно, по желанию, включать и отключать
У меня есть три набора значений заголовка Feature-Policy
, которыми я периодически пользуюсь:
oversized-images 'none'; unoptimized-lossy-images 'none'; unoptimized-lossless-images 'none';
unsized-media 'none'; oversized-images 'none'; unoptimized-lossy-images 'none'; unoptimized-lossless-images 'none';
script-sync 'none'; unsized-media 'none'; oversized-images 'none'; unoptimized-lossy-images 'none'; unoptimized-lossless-images 'none';
Я часто держу включенным первый из них. Очень увлекательно путешествовать по веб-страницам, к которым применяются использованные в нём политики. Страшно смотреть на то, как велики некоторые изображения. В современном вебе очень много такого, что можно было бы улучшить.
Так, на страницах очень и очень часто срабатывает политика unsized-media
(и я этим тоже грешу), поэтому её использование при обычной работе в интернете доставляет неудобства. Именно поэтому она у меня выделена в отдельную политику, которую я могу включать и выключать. То же самое касается и политики sync-scripts
, применение которой «ломает» множество сайтов.
Некоторые команды, с которыми я работал, начали использовать описанные мной политики в ходе разработки. Это позволяет им быстро обращать внимание на ранее незаметные проблемы. Конечно, я рекомендую включать все политики Feature-Policy
в среде разработки, что позволяет быстро выявлять и исправлять ошибки.
Итоги
Надеюсь, что поддержка Feature-Policy
появится, в итоге, не только в Chrome. Хотя это — мой основной браузер, мне приходится пользоваться и другими браузерами. Было бы полезно, если бы и они поддерживали Feature-Policy
. Но тут, правда, мы видим ту редко встречающуюся ситуацию, когда даже экспериментальной поддержки некоей возможности достаточно для того, чтобы она могла бы принести реальную пользу.
Незаметные проблемы с производительностью — это тоже проблемы. И возможность сделать такие проблемы заметнее — это один из лучших методов, которым может воспользоваться разработчик для того, чтобы их выявлять и исправлять.
Уважаемые читатели! Планируете ли вы пользоваться заголовком Feature-Policy для выявления незаметных проблем веб-проектов?