Недавно я снова исследовал вопрос загрузки шрифтов, так как я хотел использовать локальную копию шрифта и сделать ее загрузку максимально быстрой и эффективной. Данный подход существенно отличается от того, когда вы используете TypeKit или шрифты Google и простые сниппеты «копировать/вставить».
За последние месяцы было написано несколько статей, рассматривающих вопрос различных техник оптимизации загрузки шрифтов.
1, 2, 3
После того, как я их все прочел, я обнаружил несколько новых вопросов, которые в них не затрагиваются. В конечном итоге, я хотел иметь один ресурс, на котором бы была собрана информация о таких проблемах. Некоторые сниппеты кода взяты или адаптированы из статей, ссылки на которые я привел выше.
Цели:
А теперь давайте попробуем добиться наших целей поэтапно:
Так как вам нужно вызывать веб-шрифт локально через @font-face, обычно у вас появляется блокирующий запрос. Чтобы его избежать, вам нужно поместить код @font-face в отдельную таблицу стилей, которая вызывается через JavaScript. Можно использовать простую функцию loadCSS от filamentgroup. Это наиболее простой вариант.
Также можно воспользоваться webfontloader от typekit с пользовательскими веб-шрифтами, в которых, начиная с версии 1.6.8, используется родной API загрузки шрифтов, если он доступен.
На данный момент у нас также есть Font Face Observer, а еще Font Loader, в котором используются свежие события загрузки шрифтов W3C, а также у нас есть скрипт, который использует Smashing Magazine, и который, по сути, решает еще несколько проблем этой статьи (но не все).
Так с чего же нам начать? Для начала, мы исключим Font Loader, обеспечивая полифил для новейшего веб-стандарта. Он слишком большой, чтобы включать его в эту часть этапа оптимизации производительности. В дальнейшем он определенно будет нужен, но, если он не «родной», в данный момент нет смысла его использовать. Вместо этого мы можем воспользоваться Font Face Observer. Он весит всего 5Кб (он уменьшен, а не сжат).
Хорошо, давайте начнем. Получить скрипт Font Face Observer можно через npm, bower или git(hub). Включите скрипт в head.
Примечание: Произвольная настройка атрибута async добавляет нежелательный FOIT к FOUT на вашем сайте.
Теперь нам нужно инициализировать обозреватель шрифта. Это можно сделать с помощью встроенного скрипта, или внешнего скрипта, включенного в head. Я покажу пример, в котором используется вариант browserify/CommonJS во внешнем скрипте, и скрипт загружен в качестве зависимости npm:
Для начала нам нужно запросить библиотеку:
Теперь давайте определим шрифты и варианты шрифта, которые нам нужно выявить:
Здесь в качестве вариантов можно использовать weight, style, variant, featureSettings, stretch. Теперь нам нужно инициализировать Observer для настройки определенных нами шрифтов. Также нам нужно установить класс CSS, в который был загружен шрифт. Это делается через Promise:
Таким образом, мы применили Font Face Observer и установили класс готового шрифта. Теперь нам нужно отразить это в нашем CSS:
Теперь у нас есть эффективная и надежная система, в которую можно будет внедрить наш веб-шрифт, когда он будет готов. Это, конечно же, значит, что изначально пользователи получат определенный вами переходной шрифт. Несмотря на то, что это неплохо, так как они могут уже начать читать текст, это означает, что у них перед глазами все изменится, что зачастую приводит к смещению слов из-за изменения шрифта.
Нам нужно сделать опыт более приятным. Это можно сделать в два этапа:
Этот пункт довольно прост. Фактически, мы уже сделали многое для того, чтобы наши веб-шрифты загружались максимально быстро и включались в нужное время. Последнее, что мы можем здесь сделать, это добавить заголовок preload для файлов веб-шрифтов.
Как вы видите, здесь я использовал только шрифты woff. Это потому, что здесь нам не стоит вызывать все форматы. Этот небольшой сниппет говорит браузеру, что, если это возможно, он должен загрузить эти ресурсы. Если вы добавите здесь все форматы, браузер будет загружать их все, что бесполезно. Однако, все браузеры, которые поддерживают prefetch, также поддерживают WOFF. Если вы хотите использовать woff2, учтите, что он уже не работает, поэтому следует избегать добавления этих заголовков.
Чтобы исключить ситуацию, когда пользователи повторно загружают веб-шрифты и не запускают последний вариант JavaScript Font Face Observer, когда шрифт уже доступен из кэша, можно использовать куки или localStorage. Если, используя куки, вы проверяет только то, есть ли этот шрифт в кэше браузера, то localStorage помещает шрифты на более длительное хранение в localStorage и достает их оттуда, если пользователь снова заходит на сайт.
Я не буду никому отдавать предпочтение, так как у обоих вариантов есть свои преимущества. Несмотря на то, что localStorage более надежен с точки зрения кэширования шрифта, он немного медленнее, чем куки и родной кэш браузера. Используйте то, что больше нравится вам, и что будет лучше работать в вашем проекте.
Использование cookies:
Этот метод немного ненадежен. Кэш браузера не связан напрямую с местом хранения куки, поэтому, если один из них (но не оба) удален, этот метод может привести к блокированию загрузки шрифта.
самый простой код, использующий SSI, будет выглядеть следующим образом:
Использование localStorage: можно воспользоваться этим вариантом кода, который Smashing Magazine использует на своем сайте.
Несмотря на то, что localStorage работает довольно неплохо, стоит учитывать, что его использование может оказаться немного более медленным, чем использование родного кэша, и, что более важно, он занимает место на диске пользователя, и создается только для одного домена. Поэтому, если пользователь зайдет на другой сайт с таким же веб-шрифтом, UA будет загружать его заново.
Как мы заметили за последние месяцы и годы, способ использования веб-шрифтов все время изменяется. Браузеры используют Font Load Events, а также могут использоваться даже свойства CSS, которые помогают работать с FOIT. В конечном итоге, font-rendering, font-rendering: swap могут значительно упростить все то, что я описал в этой статье.
Если вы хотите сэкономить несколько байтов, исключив некоторые стили или варианты насыщенности, можете попробовать настроить поведение браузера, если в нем допускаются искусственные шрифты и нет font-synthesis.
За последние месяцы было написано несколько статей, рассматривающих вопрос различных техник оптимизации загрузки шрифтов.
1, 2, 3
После того, как я их все прочел, я обнаружил несколько новых вопросов, которые в них не затрагиваются. В конечном итоге, я хотел иметь один ресурс, на котором бы была собрана информация о таких проблемах. Некоторые сниппеты кода взяты или адаптированы из статей, ссылки на которые я привел выше.
Цели:
- Асинхронно загружать веб-шрифты
- Избежать сильного пересчета положения в макете
- Как можно быстрее загружать веб-шрифты
- Избежать загрузки шрифтов для возвращающихся посетителей
А теперь давайте попробуем добиться наших целей поэтапно:
1. Асинхронная загрузка веб-шрифтов
Так как вам нужно вызывать веб-шрифт локально через @font-face, обычно у вас появляется блокирующий запрос. Чтобы его избежать, вам нужно поместить код @font-face в отдельную таблицу стилей, которая вызывается через JavaScript. Можно использовать простую функцию loadCSS от filamentgroup. Это наиболее простой вариант.
Также можно воспользоваться webfontloader от typekit с пользовательскими веб-шрифтами, в которых, начиная с версии 1.6.8, используется родной API загрузки шрифтов, если он доступен.
На данный момент у нас также есть Font Face Observer, а еще Font Loader, в котором используются свежие события загрузки шрифтов W3C, а также у нас есть скрипт, который использует Smashing Magazine, и который, по сути, решает еще несколько проблем этой статьи (но не все).
Так с чего же нам начать? Для начала, мы исключим Font Loader, обеспечивая полифил для новейшего веб-стандарта. Он слишком большой, чтобы включать его в эту часть этапа оптимизации производительности. В дальнейшем он определенно будет нужен, но, если он не «родной», в данный момент нет смысла его использовать. Вместо этого мы можем воспользоваться Font Face Observer. Он весит всего 5Кб (он уменьшен, а не сжат).
Хорошо, давайте начнем. Получить скрипт Font Face Observer можно через npm, bower или git(hub). Включите скрипт в head.
Примечание: Произвольная настройка атрибута async добавляет нежелательный FOIT к FOUT на вашем сайте.
Теперь нам нужно инициализировать обозреватель шрифта. Это можно сделать с помощью встроенного скрипта, или внешнего скрипта, включенного в head. Я покажу пример, в котором используется вариант browserify/CommonJS во внешнем скрипте, и скрипт загружен в качестве зависимости npm:
Для начала нам нужно запросить библиотеку:
var FontFaceObserver = require('fontfaceobserver');
Теперь давайте определим шрифты и варианты шрифта, которые нам нужно выявить:
var observer = new FontFaceObserver('Fira Sans', {
weight: 400
});
Здесь в качестве вариантов можно использовать weight, style, variant, featureSettings, stretch. Теперь нам нужно инициализировать Observer для настройки определенных нами шрифтов. Также нам нужно установить класс CSS, в который был загружен шрифт. Это делается через Promise:
observer.check().then(function () {
document.documentElement.classList.add('webfont-loaded');
}, function () {
console.info('Web font could not be loaded in time. Falling back to system fonts.');
});
Более расширенный пример с несколькими веб-шрифтами:
var fontfaceobserver = require('fontfaceobserver');
var fontObservers = [];
var fontFamilies = {
'Fira Sans': [
{
weight: 400
},
{
weight: 600
}
],
'Fira Mono': [
{
weight: 400
}
]
}
Object.keys(fontFamilies).forEach(function(family) {
fontObservers.push(fontFamilies[family].map(function(config) {
return new FontFaceObserver(family, config).check()
}));
});
Promise.all(fontObservers)
.then(function() {
document.documentElement.classList.add('webfont-loaded');
}, function() {
console.info('Web fonts could not be loaded in time. Falling back to system fonts.');
});
Таким образом, мы применили Font Face Observer и установили класс готового шрифта. Теперь нам нужно отразить это в нашем CSS:
body {
font-family: sans-serif;
}
.webfont-loaded body {
font-family: 'Fira Sans', sans-serif;
}
2. Устранение сильного пересчета положения в макете
Теперь у нас есть эффективная и надежная система, в которую можно будет внедрить наш веб-шрифт, когда он будет готов. Это, конечно же, значит, что изначально пользователи получат определенный вами переходной шрифт. Несмотря на то, что это неплохо, так как они могут уже начать читать текст, это означает, что у них перед глазами все изменится, что зачастую приводит к смещению слов из-за изменения шрифта.
Нам нужно сделать опыт более приятным. Это можно сделать в два этапа:
- Сначала вам нужно найти наилучшие переходные шрифты для вашего веб-шрифта. Лучше всего это можно сделать с помощью этого небольшого букмарклета и этой проверки совместимости. Когда вы найдете хороший переходной шрифт, полдела сделано. Но не существует ни одного шрифта, который выглядит так же, и даже очень похожие переходные шрифты могут иметь знаки разной ширины и высоты. К счастью, в CSS есть несколько инструментов, которые позволяют это оптимизировать. Есть новое свойство font-size-adjust, позволяющее подстроить переходной шрифт к вашему веб-шрифту или наоборот. Воспользуйтесь им, чтобы настроить высоту или форму. Его довольно сложно использовать, поэтому я рекомендую попробовать некоторые значения в dev tools (или посмотрите формулу в статье, на которую я дал ссылку, если вы хорошо понимаете математику). Обычно в результате получается значение между 0,40 и 0,70.
- После подбора высоты, нужно настроить letter-spacing (расстояние между буквами). Это относительно просто, и, если настраивать значение с небольшим шагом (например, -0,01em), вы сможете гарантировать, что текст будет выглядеть хорошо и читабельно. На этом этапе вы должны получить визуализацию переходного шрифта, очень похожую на веб-шрифт. Чтобы в процессе тестирования почувствовать разницу, можете добавить таймаут в функцию, которая задает CSS-класс для webfont-loaded .
3. Максимально быстрая загрузка веб-шрифтов
Этот пункт довольно прост. Фактически, мы уже сделали многое для того, чтобы наши веб-шрифты загружались максимально быстро и включались в нужное время. Последнее, что мы можем здесь сделать, это добавить заголовок preload для файлов веб-шрифтов.
<link rel="preload" href="FiraSans-Regular.woff">
<link rel="preload" href="FiraSans-SemiBold.woff">
Как вы видите, здесь я использовал только шрифты woff. Это потому, что здесь нам не стоит вызывать все форматы. Этот небольшой сниппет говорит браузеру, что, если это возможно, он должен загрузить эти ресурсы. Если вы добавите здесь все форматы, браузер будет загружать их все, что бесполезно. Однако, все браузеры, которые поддерживают prefetch, также поддерживают WOFF. Если вы хотите использовать woff2, учтите, что он уже не работает, поэтому следует избегать добавления этих заголовков.
4. Исключение загрузки шрифтов для возвращающихся посетителей
Чтобы исключить ситуацию, когда пользователи повторно загружают веб-шрифты и не запускают последний вариант JavaScript Font Face Observer, когда шрифт уже доступен из кэша, можно использовать куки или localStorage. Если, используя куки, вы проверяет только то, есть ли этот шрифт в кэше браузера, то localStorage помещает шрифты на более длительное хранение в localStorage и достает их оттуда, если пользователь снова заходит на сайт.
Я не буду никому отдавать предпочтение, так как у обоих вариантов есть свои преимущества. Несмотря на то, что localStorage более надежен с точки зрения кэширования шрифта, он немного медленнее, чем куки и родной кэш браузера. Используйте то, что больше нравится вам, и что будет лучше работать в вашем проекте.
Использование cookies:
Этот метод немного ненадежен. Кэш браузера не связан напрямую с местом хранения куки, поэтому, если один из них (но не оба) удален, этот метод может привести к блокированию загрузки шрифта.
самый простой код, использующий SSI, будет выглядеть следующим образом:
<!--#if expr="$HTTP_COOKIE=/webfont-loaded=true/" -->
<html lang="en" class="webfont-loaded">
<!--#else -->
<html lang="en">
<!--#endif -->
Использование localStorage: можно воспользоваться этим вариантом кода, который Smashing Magazine использует на своем сайте.
Несмотря на то, что localStorage работает довольно неплохо, стоит учитывать, что его использование может оказаться немного более медленным, чем использование родного кэша, и, что более важно, он занимает место на диске пользователя, и создается только для одного домена. Поэтому, если пользователь зайдет на другой сайт с таким же веб-шрифтом, UA будет загружать его заново.
Что дальше?
Как мы заметили за последние месяцы и годы, способ использования веб-шрифтов все время изменяется. Браузеры используют Font Load Events, а также могут использоваться даже свойства CSS, которые помогают работать с FOIT. В конечном итоге, font-rendering, font-rendering: swap могут значительно упростить все то, что я описал в этой статье.
Если вы хотите сэкономить несколько байтов, исключив некоторые стили или варианты насыщенности, можете попробовать настроить поведение браузера, если в нем допускаются искусственные шрифты и нет font-synthesis.
Платежные решения Paysto для читателей Хабра:
> Получите оплату банковской картой прямо сейчас. Без сайта, ИП и ООО.
> Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
> Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
> Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.
> Принимайте оплату от компаний через Интернет. Без сайта, ИП и ООО.
> Приём платежей от компаний для Вашего сайта. С документооборотом и обменом оригиналами.
> Автоматизация продаж и обслуживание сделок с юр.лицами. Без посредника в расчетах.
Комментарии (5)
mvs
16.10.2015 18:13+1Внешний js-загрузчик, внешний FontFaceObserver, дополнительные переходные шрифты и дополнительные переходные стили, cookies/localStorage и серверный ssi — а теперь со всем этим **** мы попытаемся взлететь!
Т.е. вы правда считаете это самым лучшим способом загрузки шрифтов и он будет быстрее обычного нативного подключения со всеми его недостатаками?
jemali_m
До Нового года еще 47 дней !) В целом, — спасибо за полезные ссылки.
Kudja
т.е. декабрь мы уже не считаем )))
Yahweh
Вы что не в курсе? В этом году перевод часов на 744 часа вперед.
jemali_m
Net,… noyabr' ))