Главная » Статьи » Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

От автора: типографика в Интернете прошла долгий путь со времен Scalable Inman Flash Replacement (sIFR) и более поздних версий cufón. Это были тяжелые времена для веб-разработчиков. Раньше я боялся получить PSD-файл с каким-то экзотическим шрифтом, использованным в дизайне, так как знал, что впереди еще много часов кросс-браузерной настройки.

К счастью, в современном Интернете у нас есть правило CSS @font-face, которое позволяет указывать собственный шрифт при отображении текста. Это не всегда было гладко, но сегодня, в 2020 году, многие проблемы устранены, и поддержка шрифтов WOFF2 в браузере выглядит действительно хорошо. Так в чем же подвох? Веб-производительность.

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

Шрифты и веб-производительность

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

Так что, если шрифт загружается, что делает браузер с контентом? Ну, как правило, браузеры отображали невидимый шрифт-заполнитель, а затем заменяли его фактическим шрифтом после его загрузки. Этот метод известен как Вспышка невидимого текста (FOIT). Это не идеально, но, по крайней мере, пользователь может видеть что-то правильно? Но что произойдет, если у пользователя медленное соединение и шрифт загружается за 10 секунд? Или что, если шрифт никогда не загрузится вообще? Пользователю останется смотреть на веб-сайт, который выглядит следующим образом:

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

В приведенном выше примере пользователь должен ждать 5 секунд, прежде чем в окне просмотра будет отображен какой-либо текст. Это долгое время, учитывая, что исследование Google показало — 53% посетителей мобильного сайта покинут его, если сайт не загрузится в течение трех секунд. Итак, что мы можем сделать, чтобы решить эту проблему производительности? font-display приходит на помощь!

Свойство font-display

Свойство font-display определено в спецификации CSS Fonts Module Level 4. Оно позволяет разработчику выбирать, что происходит на этапе отображения шрифта при загрузке страницы. Просто чтобы все на 100% прояснить — это не влияет на фактическую загрузку самого шрифта по сети, а на то, что происходит с типографикой на странице во время этой фазы загрузки. Свойство имеет пять возможных значений, давайте кратко ассмотрим их:

Block

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

Fallback

Согласно спецификации: [Fallback] задает чрезвычайно малый период блокировки (в большинстве случаев рекомендуется 100 мс или менее) и короткий период замены (в большинстве случаев рекомендуется 3 с).

Таким образом, при загрузке страницы невидимый текст может отображаться до 100 мс (известно, как период блокировки). После 100 мс отображается следующий шрифт в стеке шрифтов CSS (например, Arial, Helvetica Neue, Helvetica, sans-serif). Затем браузеру дается 3 секунды для замены резервного шрифта на веб-шрифт. По истечении 3 секунд резервный шрифт будет использоваться до конца жизни страницы, и веб-шрифт никогда не будет отображаться (даже если он успешно загрузится).

3-секундный предел — превосходная настройка, если вы знаете, что у вас пользователи с медленным соединением. Представьте, что страница загружается на медленном устройстве. Резервный шрифт отображается, поэтому пользователь начинает читать содержимое. На чтение у него может уйти 5–10 секунд, а затем внезапно загружается веб-шрифт (о котором он ничего не знает). Если метрики шрифта веб-шрифта и резервного шрифта различаются, это может привести к большому скачку страницы, что довольно неприятно.

Исходя из моих наблюдений (как будет видно далее в статье), практическое применение спецификации дословно ей соответствует. Невидимый текст заполнитель будет отображаться до 100мс, после чего выводится резервный шрифт.

Swap

В спецификации говорится: [Swap] задает чрезвычайно малый период блока (в большинстве случаев рекомендуется 100 мс или менее) и бесконечный период замены.

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

Но практическая реализация в браузерах отличается. У swap нет периода блокировки. Резервный шрифт отображается, как только страница визуализирована. Таким образом, несмотря на то, что в спецификации fallback и swap описываются очень похоже, они на самом деле отличаются с точки зрения воспринимаемой производительности.

Optional

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

Я заинтригован тем, почему этот параметр не используется более широко, учитывая его очевидное преимущество как для удобства использования веб-сайта, так и для улучшения объема загружаемых данных для медленных соединений. Но если бы я рискнул делать предположения, я бы сказал, что это потому, что оно очень строго с точки зрения перерисовки. Если отображается запасной шрифт, веб-шрифт никогда не будет отображен, даже если он загружен быстро. Таким образом, это приводит к тому, что для пользователей на быстром устройстве и соединении, будет отображен запасной вариант, а основной веб-шрифт будет загружен, но не отображен. Веб-шрифт отображается только в том случае, если пользователь переходит на другую страницу. Поэтому я думаю, что часть дизайнеров будут раздражены, если выбранный ими веб-шрифт не будет виден при начальной загрузке страницы!

Auto

Очень просто для понимания. Auto будет использовать любую настройку, которую пользовательский агент уже определил. Для многих браузеров это будет block.

Поддержка браузерами

Поддержка браузерами для font-display самом деле выглядит великолепно, примерно 82% пользователей по всему миру используют браузеры, которые его поддерживают. Несколько удивительных для меня вещей:

Samsung Internet

Samsung Internet (SI) — это браузер, завоевавший определенную долю рынка на устройствах Android. В настоящее время он не поддерживает font-display. Я подозреваю, что это потому, что SI v10.x основан на Chrome 71 (который не поддерживает font-display). В Интернете ходят слухи, что v11.x будет основана на Chrome 75, который поддерживает font-display. Так что поддержка может быть уже не за горами.

Edge (Chromium)

Оказывается, данные в caniuse были неверны. Как доказывает этот тест, Edge поддерживает font-display, как через @font-face, так и через API. Я открыл тему с проблемой, и она была решена менее чем за 10 часов. Сила открытых источников снова поражает! Если вы сейчас посмотрите FontFace API: display, вы увидите правильные значения. Я оставляю это примечание, чтобы напомнить читателям, что вы не всегда можете доверять своим источникам! Благодарю Зак Лизермана за помощь в прояснении этой загадки, и vinyldarkscratch, Fyrd за исправления.

Тестирование воспринимаемой производительности

Итак, как мы можем проверить влияние различных настроек font-display на воспринимаемую производительность веб-сайтов? Один из способов — просто обновить правило @font-face, развернуть его и запустить через WebPageTest. Это работает, но есть и другой способ, который может упростить этот рабочий процесс.

И все благодаря использованию функционала WebPageTest «Inject Script» (на вкладке «Дополнительно») и использованию API загрузки шрифтов CSS.

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Здесь мы используем метод, очень похожий на тот, который демонстрирует Энди Дэвис в своем посте «Улучшение воспринимаемой производительности с помощью заголовка HTTP Rel = preconnect».

Ниже вы можете найти упрощенный код, который можно использовать для данной задачи, или см. Gist здесь:

(function(){ // this will trigger a font load var customFont1 = new FontFace('custom font name', 'url([FONT_URL_HERE])', { display: 'block', // display setting to test here weight: '700' // font-weight // other font properties here }); // IMPORTANT: add the font to the document document.fonts.add(customFont1); // monitor the font load (optional) customFont1.loaded.then((fontFace) => { // log some info in the console console.info('Font status:', customFont1.status); // optional // log some info on the WPT waterfall chart performance.mark('wpt.customFont1Loaded'); // optional }, (fontFace) => { // if there's an error, tell us about it console.error('Font status:', customFont1.status); // optional }); // repeat above for multiple fonts })();

Измените приведенный выше код и вставьте его в текстовую область «Inject Script». Этот код будет введен на тестовую страницу перед выполнением теста и загрузит и определит новый шрифт (с тем же font-family) с обновленными настройками.

Ключом к тому, чтобы это решение работало, является обеспечение добавления вручную шрифта после регистрации @font-face шрифтов. Приоритет шрифтов с одинаковым именем font-family: последний имеет наивысший приоритет. Это соответствует концепции CSS, например, если у вас есть два селектора с одинаковой специфичностью, тот, который указан последним, «перезаписывает» первый.

Давайте посмотрим на гипотетический график тестирования:

Запускается WebPageTest, браузер согласовывает соединение с сервером и запрашивает HTML.

HTML загружается и анализируется, запрашиваются другие ресурсы страницы, такие как CSS и JS.

На этом этапе WPT включает и выполняет JavaScript. Наш новый шрифт определяется и загружается с использованием API загрузки шрифтов.

CSS загружается и анализируется, он также содержит правило @font-face с той же базовой информацией (имя шрифта, URL шрифта и т. д.).

Объект FontFace и @font-face являются связным с CSS. Синтаксический анализ CSS добавляет шрифт в конец списка (он последний), поэтому он имеет высший приоритет, чем наш вручную добавленный шрифт.

Страница загружается как обычно, изменений от включенного скрипта не наблюдается.

Точка 3 является непредсказуемой частью этого процесса. Нет никаких гарантий, что введенный скрипт будет запущен после загрузки / анализа CSS. Это может отличаться в зависимости от браузера. Так что, если приведенный выше код не работает у вас при настройке сайта, что еще вы можете попробовать?

Решения

Ну, есть два решения, которые я нашел, чтобы исправить эту проблему:

Блокировать CSS шрифт

Это менее, чем идеально, но это работает. Если у вас есть правила @font-face, определенные в отдельном файле CSS, вы можете просто использовать функционал WebPageTest ‘block’ для блокировки CSS шрифта:

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

В этой ситуации происходит то, что наш скрипт внедрения создает все соответствующие объекты FontFace, и они не подвергаются деприоретизации по правилам CSS @font-face. Есть несколько основных проблем, связанных с этим методом:

Что произойдет, если у вас много шрифтов? Вам необходимо скопировать их все в скрипт внедрения.

Как насчет того, чтобы CSS шрифта объединялся с другим CSS? По сути, вам нужно заблокировать все стили CSS страницы.

Так что да, этот метод мог бы работать, но он далек от совершенства.

Определить загрузку CSS

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

Лучший способ, который я нашел, это использовать тот факт, что вы с помощью небольшого JavaScript можете обнаружить, когда файл CSS загружен. Для этого можно изменить скрипт внедрения. Вот Gist с измененным кодом:

(function(){ // create our custom link tag for the stylesheet var url = "https://www.example.com/static/app.css"; // IMPORTANT: this is the CSS file that contains your @font-face rules var head = document.getElementsByTagName('head')[0]; var link = document.createElement('link'); link.type = "text/css"; link.rel = "stylesheet" link.href = url; // append the stylesheet to the head head.appendChild(link); // wait for the CSS file to load before modifying the font setup link.onload = function () { // define our font face and modify the properties (will trigger a load) var customFont1 = new FontFace('nta', 'url([FONT_URL_HERE])', { display: 'swap', // display setting to test here weight: '700' // font-weight // other font properties here }); // IMPORTANT: add the modified font to the FontFaceSet document.fonts.add(customFont1); // monitor the font load (optional) customFont1.loaded.then((fontFace) => { // log some info in the console console.info('Font status:', customFont1.status); // optional // log some info on the WPT waterfall chart performance.mark('wpt.customFont1Loaded'); // optional }, (fontFace) => { // if there's an error, tell us about it console.error('Font status:', customFont1.status); // optional }); // repeat above for multiple fonts }
})();

С помощью этого скрипта мы подключаемся к событию загрузки CSS, который содержит правила @font-face (это важно). Итак, вот что происходит в браузере:

Запускается WebPageTest, браузер согласовывает соединение с сервером и запрашивает HTML.

HTML загружается и анализируется, запрашиваются другие ресурсы страницы, такие как CSS и JS.

На этом этапе WPT внедряет JavaScript, и он может выполняться. Если это так, мы создаем копию элемента <link>, который загружает наш CSS @font-face, и ждем его загрузки.

CSS загружается и анализируется. Браузер создает объекты FontFace для правил CSS @font-face (связанные с CSS).

Запускаются события загрузки CSS, и наши пользовательские объекты FontFace добавляются в FontFaceSet последними, поэтому они имеют приоритет перед настройками шрифта, связанными с CSS (например, свойством font-display).

Страница загружается с нашими измененными настройками шрифта.

И мы достигли своей цели. Получили быстрый способ проверить различные настройки font-display на рабочем веб-сайте и наблюдать результаты в WebPageTest.

Отладка теста WebPageTest

Это действительно полезный совет от Энди Дэвиса, который я немного модифицировал для этих примеров. Если вы хотите увидеть, каков в тесте вывод в консоль браузера, вставьте следующий код в текстовое поле «Inject Script»:

// hijack the console.log method, allowing you to log all console massages
window._consoleMessages = [];
console.log = function(m){_consoleMessages.push(m)}; // specifically used for this font example to 'copy' the `FontFace` object
function flatten(obj) { var result = {}; for(var key in obj) { result[key] = obj[key]; } return result;
}

Затем в поле «Custom Metrics» раздела «Custom» введите следующее:

[consoleMessages]
for(var font of document.fonts){ // push results into the hijacked console // could easily return a simple array though if required console.log(flatten(font));
} return JSON.stringify(_consoleMessages, null, 2);

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Это даст вам вывод, который выглядит примерно так:

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Очень полезно, если вы ищете конкретную информацию о браузере в тестовом прогоне.

Давайте посмотрим на это в действии.

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

Стандартная страница с одним веб-шрифтом, загружаемым с помощью правила CSS @font-face. Это наш контрольный образец (font-display: auto) — ссылка.

Стандартная страница + тот же веб-шрифт, добавленный во второй раз с использованием API загрузки шрифтов, с установленным font-display: swap — ссылка.

Стандартная страница + тот же веб-шрифт, добавленный во второй раз с использованием API загрузки шрифтов, с установленным font-display: fallback — ссылка.

Стандартная страница + тот же веб-шрифт, добавленный во второй раз с использованием API загрузки шрифтов, с установленным font-display: optional — ссылка.

Страницы 2, 3 и 4 являются проверкой концепции. Каждая из них включает вышеуказанный скрипт после файла fonts.css. Таким образом, на этих страницах шрифт будет добавлен в FontFaceSet вручную вместе с новым свойством font-display. Покажет ли это какие-либо изменения в WebPageTest? ПРИМЕЧАНИЕ. Веб-шрифт применяется только к заголовку (h1) на страницах, поэтому вам следует сосредоточиться на следующих тестах.

Улучшение воспринимаемой производительности с помощью свойства CSS font-display

Вы можете более подробно рассмотреть сравнение для 4 страниц здесь, но вот результаты, как я их вижу:

Optional: Заголовок отображается в начале невидимым текстом заполнителем. Спустя ~ 100 мс отображается резервный текст. Веб-шрифт никогда не отображается.

Fallback: Заголовок отображается в начале невидимым текстом заполнителем. Спустя ~ 100 мс отображается резервный шрифт. Шрифт загружается в течение 3 секунд, поэтому он заменяется.

Swap: Заголовок немедленно отображается видимым резервным шрифтом (например, период блокировки 0s). Резервный шрифт заменяется на веб-шрифт после его загрузки.

Auto: как и ожидалось, установлено значение block. Отображается невидимый текст заполнитель, текст не отображается, пока веб-шрифт не загружен.

Все вышеперечисленные результаты являются ожидаемыми для каждого значения font-display, и тот факт, что мы видим разницу, показывает, что шрифт, добавленный программным способом через API загрузки шрифтов CSS, может изменить страницу. Итак, давайте сделаем еще один шаг вперед. Давайте сделаем так, чтобы WebPageTest внедрил скрипт.

В этих тестах мы используем контрольную страницу во всех случаях, а затем получаем WebPageTest для внедрения скрипта с каждым из различных значений font-display. Полное сравнение результатов этих тестов можно увидеть здесь.

Это выглядит удивительно похоже на изображение выше, не так ли? Фактически, при ближайшем рассмотрении мы видим точно такие же результаты, что и для приведенных выше страниц скриптов, включенных вручную. Таким образом, мы можем фактически использовать WebPageTest для изменения свойства font-display (и на самом деле любого свойства, которое не является только для чтения). Я уверен, что другие люди тоже могут придумать другие применения для этой техники. Попробуйте сами: посмотрите этот Gist со ссылкой на тестовую страницу и скрипты.

Заключение

И вот, мы получили способ изменить настройки font-display страницы при использовании WebPageTest. Не нужно вручную обновлять код, чтобы увидеть, как это свойство влияет на воспринимаемую производительность страниц. Я хотел бы услышать ваши идеи и / или предложения.

Источник: https://nooshu.github.io

Редакция: Команда webformyself.