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

Задачи

  1. Пользователи должны увидеть текст настолько быстро, насколько возможно.
  2. Пока шрифт грузится, текст должен быть отрисован запасным шрифтом, чтобы пользователи смогли его увидеть и прочитать.
  3. Нельзя раздражать пользователей мерцанием текста при каждой загрузке страницы.
  4. Современные браузеры, поддерживающие формат WOFF2, должны получать файлы в WOFF2, размер которых примерно на 30% меньше.

Как это сделать

  • Асинхронная загрузка шрифтов как CSS решает задачи №1 и №2. Но к сожалению, вызывает мерцание при каждой загрузке страницы. Браузеры уже отрисовали текст запасным шрифтом к тому моменту, когда загрузился веб-шрифт. Поэтому они заменяют тексты, что вызывает некоторое мерцание.
  • Идея состоит в том, чтобы загружать шрифт с сервера только один раз. После этого мы сохраняем данные в localStorage, и при последующих запросах мы будем загружать шрифт непосредственно оттуда. Это уберёт мерцание при повторных загрузках, оно останется только при первом открытии страницы. Таким образом, задача №3 также решается.
  • Довольно сложно распознать поддержку WOFF2 в браузере, если мы не хотим полагаться на распознавание User Agent. Лучшее, что я смог найти — очень продуманный скрипт от Filament Group, использующий API загрузки шрифтов. Он не решает задачу на все 100%, однако не даёт неправильных результатов, что приемлемо в нашем случае.

Обзор скрипта

  1. Для старых браузеров мы сразу остановим загрузку. Проверки window.addEventListener или некоторых известных User Agent (старый Android браузер, Opera Mini и тому подобное) будет достаточно.
  2. Иногда localStorage может быть недоступен, несмотря на поддержку браузером WOFF-шрифтов. На этот случай я делаю запасной вариант.
  3. Затем мы проверяем, был ли шрифт сохранён в localStorage. Если был, загружаем его немедленно.
  4. Если шрифт не был загружен ранее, получаем его Ajax-запросом. Но перед этим проверяем поддержку WOFF2.
  5. Затем сохраняем данные в localStorage и добавляем CSS-код в элемент <style>.

Давайте взглянем на скрипт

Скрипт и несколько Grunt-задач по минификации доступны по ссылке bdadam / OptimizedWebfontLoading. Также вы найдёте там демонстрации.

Где разместить скрипт

Скрипт должен находиться в <head> разделе вашей страницы над всеми объявлениями стилей, так, чтобы это не блокировало отрисовку страницы (браузер не должен ждать, пока CSSOM станет готова).

Что происходит, когда localStorage недоступен?

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

Запасной шрифт

Я считаю, что достаточно подключать веб-шрифты лишь для тех браузеров, которые поддерживают WOFF или WOFF2 форматы. Это примерно 90% всех пользователей в мире. Другие браузеры должны получать текст, отрисованный запасным шрифтом.

Пользователи со старыми браузерами будут благодарны за то, что мы не тратим их ограниченные ресурсы (ЦПУ, память) на развлекательные нужды.

Демонстрация

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

  1. Загрузка веб-шрифтов как CSS (асинхронно), затем размещение в localStorage для последующих запросов.
  2. Загрузка веб-шрифтов как CSS (асинхронно), но без использования localStorage.
  3. Загрузка веб-шрифтов из внешних WOFF и WOFF2 файлов.

Сравнение

Я провёл некоторые тесты на webpagetest.org с 3G соединением.

Первое посещение было просто-напросто одинаковым для решений с localStorage и асинхронным CSS. Оба сначала отрисовали текст запасным шрифтом, затем переключились на веб-шрифт, вызвав мерцание в первый раз. Загрузка шрифтов из внешних файлов привела к невидимости текста до тех пор, пока шрифты не были загружены. Это затормозило отрисовку на 0.6 секунды.

№1 и №2 лидируют.

Сравнения от второго посещения также показывают некоторые различия между localStorage и асинхронным CSS. Мы можем ясно видеть, что загрузка из localStorage не вызывает никакой перерисовки. Как только HTML загружен, страница отрисовывается немедленно.

Метод асинхронного CSS отрисовывает страницу с базовым шрифтом, а затем перерисовывает с веб-шрифтом. Это вызывает мерцание для пользователя при каждой загрузке страницы.

C учётом этого, №1 — победитель.

Скриншоты, чтобы показать эти отличия:

Асинхронная загрузка CSS с использованием localStorage. Нет перерисовки.
Асинхронная загрузка CSS с использованием localStorage. Нет перерисовки.
Асинхронная загрузка CSS без использования localStorage. Видна перерисовка.
Асинхронная загрузка CSS без использования localStorage. Видна перерисовка.
Внешняя загрузка шрифтов. Отрисовка останавливается, пока шрифты не загрузятся.
Внешняя загрузка шрифтов. Отрисовка останавливается, пока шрифты не загрузятся.

Материалы

Обновление

Как посоветовал @CrocoDillon, я добавил третий (необязательный) аргумент к new FontFace(..., ..., {}) при проверке поддержки WOFF2. Иначе некоторые браузеры выдают ошибки (Chrome 35 и 36, Opera 22 и 23). Посмотрите внимательней на пулреквест.

Это перевод статьи Адама Береша-Дика — «Better webfont loading with using localStorage and providing WOFF2 support».