Это зависит от задачи. Расскажу, как мы решаем эту проблему в книгах бюро.

Книги предназначены для длительного и многократного чтения. Мы оптимизируем загрузку шрифтов соответственно: никаких миганий, первое открытие медленное, последующие — быстрые.

Долгое время мы загружали веб‑шрифты, кодируя их в Base64 и встраивая прямо в ЦСС. Это медленно: шрифты весят на 30% больше, плюс уходит время на декодирование. Когда мы вынесли шрифты из ЦСС и стали загружать их в WOFF2 по HTTP/2, время до первой отрисовки сократилось на треть.

А чтобы при следующем открытии шрифты не грузились заново, явно кешируем их навечно:

cache-control:max-age=315360000
expires:Thu, 31 Dec 2037 23:55:55 GMT

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

<head>
  <title>...</title>
  ​
  <link rel="preload" as="font" type="font/woff2"
    href="https://fonts.bureau.ru/1.6/bureausans-regular.woff2"
    crossorigin>
  ​
  <link rel="preload" as="font" type="font/woff2"
    href="https://fonts.bureau.ru/1.6/bureausans-bold.woff2"
    crossorigin>
</head>
После включения предзагрузки шрифтов, время до первой отрисовки «Типографики и вёрстки» уменьшилось в среднем на секунду

Чтобы шрифты не мигали, мы дожидаемся их загрузки и готовности. Для этого в книгах есть «пробники» — элементы с заданным шрифтом и требуемой шириной.

Например, мы знаем, что слово mmm, набранное Бюросансом с кеглем в 100 пикселей, имеет ширину 238 пикселей на экране. То же слово, набранное системным шрифтом, — 250 пикселей. Значит, если ширина элемента с текстом mmm равна 238 пикселям, шрифт загружен и готов к использованию.

<style>
.fontChecker {
  position: fixed;
  z-index: -1;
  font-size: 100px;
  opacity: 0;
}
​
.fontChecker::before {
  content: 'mmm';
}
</style>
​
<div
  style="font-family: Bureausans;"
  data-min-width="237"
  data-max-width="240"
  class="fontChecker js__fontChecker">
</div>
Небольшой разброс нужен для ретины‑неретины, где ширина и начертание немного меняются

Перед запуском книга ждёт, пока размеры всех пробников не попадут в требуемые:

let fontCheckInterval
​
// Как часто проверяем пробники
const FONT_CHECK_INTERVAL = 100
​
// Максимальное время ожидания шрифтов
const MAX_FONTS_WAIT = 4000
​
const isFontLoaded = (font) => {
  const probeWidth = font.$el.width()
​
  return probeWidth >= font.minWidth && probeWidth <= font.maxWidth
}
​
const onFontsReady = (callback) => {
  const fonts = $('.js__fontChecker').map((_, el) => {
    const $el = $(el)
    ​
    return {
      $el: $el,
      minWidth: +$el.attr('data-min-width'),
      maxWidth: +$el.attr('data-max-width'),
    }
  }).get()
  ​
  let timeSpent = 0
  ​
  fontCheckInterval = setInterval(function() {
    const allFontsReady = fonts.every(isFontLoaded)
    ​
    // Если все шрифты уже готовы или
    // мы так и не дождались их загрузки,
    // отрисовываем страницу
    if (allFontsReady || timeSpent > MAX_FONTS_WAIT) {
      clearInterval(fontCheckInterval)
      callback()
    }
    ​
    timeSpent += FONT_CHECK_INTERVAL
  }, FONT_CHECK_INTERVAL)
}
​
// onFontsReady(initApp)
Первая версия опиралась на document.fonts.ready. К сожалению, в Сафари он срабатывает слишком рано: когда браузер уже загрузил шрифты, но ещё не применил их к странице

Ещё по теме

P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.

Веб‑разработка
Отправить
Поделиться
Запинить

Рекомендуем другие советы