Перейти к содержанию

Диагностика шрифтов: от запроса к фактическому начертанию

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

В этом уроке построим цепочку наблюдений и проверим объяснение неожиданного результата. Учебные сценарии не являются замерами ProfessorWeb и не обещают снижение LCP или CLS. Наша цель — научиться отвечать на конкретный вопрос: почему выбранное начертание не используется или начинает доставляться поздно?

Подготовим объект исследования

Исходный speed-lab использует системное семейство. Отсутствие запросов шрифтов здесь нормально: стенд не содержит отдельного веб-шрифта. Сначала исследуйте этот вариант, чтобы получить понятный исходный результат. Запишите браузер, операционную систему, выбранный абзац и ширину окна. На разных устройствах системное семейство может разрешаться в разные реальные шрифты.

Для второго этапа подготовьте отдельную копию стенда и подключите собственный лицензированный шрифт под именем Lab Open Sans. Объявление @font-face, выбор font-display, согласование метрик через size-adjust и условия применения preload разобраны в уроке о загрузке шрифтов и устойчивости страницы. Файлы шрифтов предоставляются отдельно; в архиве speed-lab их нет. Здесь исследуем уже настроенное подключение, сохраняя содержание статьи и её оформление между сравниваемыми посещениями.

Выберите обычный абзац, заголовок и фрагмент с латиницей или цифрами. Они представляют разные требования к начертанию. Исследование одного заголовка не объясняет поведение всего документа: жирный текст может использовать другой файл, а отдельные символы — запасное семейство. Записывайте результат для конкретного элемента, а не для страницы вообще.

Сначала проверим запрос CSS

В Console можно получить вычисленные свойства первого абзаца учебной статьи:

const paragraph = document.querySelector("main > article > p");
const style = getComputedStyle(paragraph);
console.table({
  family: style.fontFamily,
  weight: style.fontWeight,
  size: style.fontSize,
  lineHeight: style.lineHeight
});

Селектор относится к исходному speed-lab. На другой странице сначала выберите нужный элемент. Здесь fontFamily показывает цепочку семейств из вычисленного оформления. Он не сообщает, какое семейство предоставило каждый нарисованный символ. Если первым стоит Lab Open Sans, это подтверждает намерение CSS, но ещё не подтверждает использование веб-файла.

Предположим, вместо ожидаемого семейства обнаружилось system-ui. Перейдите к Elements → Styles и найдите правило, задающее это свойство. Причиной может быть другой селектор, переопределение или наследование от контейнера. Исправление доставки файла пока не обосновано: браузер сначала должен получить требование использовать нужное семейство. Именно поэтому начинаем с элемента и каскада, а не с увеличения приоритета сетевого запроса.

Найдём фактически использованный шрифт

В Chrome выберите тот же абзац в Elements, откройте Computed и найдите блок Rendered Fonts. Он помогает увидеть реальные шрифты выбранного текста и различить локальный источник и сетевой ресурс. Такой способ проверки описан в документации Chrome DevTools.

Сравните эту запись с fontFamily. Если CSS требует Lab Open Sans, а абзац нарисован системным шрифтом, теперь есть наблюдаемое расхождение. Возможные причины ещё различаются: ресурс не получен, файл отвергнут, нужные символы отсутствуют либо режим показа оставил запасное оформление. Одна запись панели не выбирает причину автоматически.

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

У заголовка дополнительно сравните вычисленный вес и фактическое начертание. Полученный обычный файл не подтверждает наличие отдельного жирного. Сначала выясните, что именно браузер использовал, затем решайте, подходит ли такое поведение требованиям страницы. Названия панелей других браузеров отличаются; принцип проверки остаётся тем же.

Свяжем начертание с доставкой

Откройте Network до перезагрузки и включите фильтр Font. Запишите URL, статус, источник получения и инициатор запроса. Затем сравните начало запроса шрифта с доставкой таблицы стилей. Если ресурс появляется поздно, цепочка инициаторов помогает найти объявление, которое стало известно браузеру только после дополнительного CSS. Метод чтения последовательности запросов разобран в уроке о сетевом водопаде.

Отсутствие строки требует объяснения, но само по себе не доказывает поломку. В исходном стенде используется системный шрифт; в другом варианте может сработать local(), не подходить вес или не применяться правило. Сетевой ответ, в свою очередь, не доказывает успешное декодирование. Если сервер вернул страницу ошибки с кодом 200 вместо шрифтовых данных, проверяйте тело ответа, тип содержимого и сообщения Console.

Для чистого сравнения фиксируйте состояние кеша. Холодный переход и повторное посещение отвечают на разные вопросы. Если в объявлении есть local(), установленный на компьютере шрифт может скрывать путь сетевой доставки. В Chrome настройка Disable local fonts отключает именно источники local() в @font-face, а не все системные шрифты. Используйте её как отдельное условие исследования и указывайте это в записи.

Снимок Resource Timing

Чтобы связать наблюдение Network с записью ресурсов текущего документа, можно вывести отобранные записи Resource Timing:

const fontResources = performance.getEntriesByType("resource")
  .filter(entry => /\.(woff2?|ttf|otf)(?:[?#]|$)/i.test(entry.name));

console.table(fontResources.map(entry => ({
  url: entry.name,
  start: entry.startTime,
  duration: entry.duration,
  transferred: entry.transferSize,
  encoded: entry.encodedBodySize
})));

Фильтр подходит известным расширениям файлов и сохраняет адреса с параметрами. Если сервер использует URL без расширения, замените условие на сравнение с фактическим URL из Network. Пустой результат такого фильтра означает отсутствие подходящих записей, а не доказанное отсутствие всех шрифтов. Большой поток ресурсов также может исчерпать буфер записей.

startTime относится к временной шкале текущего документа, а duration описывает получение ресурса. Это не время появления текста и не готовая оценка влияния на LCP. Нулевой transferSize нельзя без дополнительных условий объявлять доказательством кеша: часть данных внешнего источника скрывается без Timing-Allow-Origin. Проверяйте URL, доступность сведений и запись Network вместе. Ограничения описаны в документации Resource Timing.

Что добавляет FontFaceSet

Набор document.fonts позволяет исследовать объявленные шрифтовые объекты и их состояние. Выведите семейство, вес и статус:

console.table([...document.fonts].map(face => ({
  family: face.family,
  weight: face.weight,
  style: face.style,
  status: face.status
})));

Это не каталог всех установленных шрифтов операционной системы и не список начертаний, реально использованных каждым абзацем. Запись loaded полезна для проверки успешной загрузки объекта, однако применять его к тексту должен CSS. Статус unloaded может относиться к объявленному, но пока ненужному начертанию. Сопоставляйте набор с ранее выбранным элементом. Различие объявленных и используемых шрифтов показано в MDN: Document.fonts.

Для снимка после завершения текущих шрифтовых загрузок и связанных операций раскладки предусмотрен document.fonts.ready. Он помогает выбрать момент повторной проверки Rendered Fonts, но не превращает запасное оформление в желаемое. Последующие изменения текста или CSS способны потребовать другие шрифты. Назначение обещания описано в MDN: FontFaceSet.ready.

Метод check() тоже требует осторожной интерпретации:

console.log(document.fonts.check('16px "Lab Open Sans"', "Пример ABC 123"));

Результат true означает, что рисование указанного текста не требует ещё незагруженных подходящих объектов из набора. Даже несуществующее семейство может дать true, поскольку набору нечего загружать. Поэтому эта проверка не доказывает, что русский абзац нарисован указанным семейством. Этот случай прямо разобран в MDN: FontFaceSet.check.

Для отдельной диагностической пробы доступен document.fonts.load(). Например, вызов document.fonts.load('16px "Lab Open Sans"', "Пример") инициирует загрузку подходящих объектов и возвращает обещание. Текст ограничивает подбор по unicode-range, но не проверяет наличие каждого глифа. Не вызывайте метод перед записью обычной загрузки: принудительная доставка меняет исследуемый путь. Условия метода описаны в MDN: FontFaceSet.load.

Проверим одну причинную гипотезу

Допустим, учебное наблюдение показало: абзац требует нужное семейство, шрифтовый объект существует, но запрос начинается после поздней таблицы стилей. Гипотеза заключается в позднем обнаружении объявления. В копии измените только расположение этого объявления, сохранив файл, текст, вес, режим показа и сетевые условия. После повторных сопоставимых посещений проверьте начало запроса и фактическое начертание. Более ранний запрос подтвердит изменение обнаружения, но влияние на показ текста потребует отдельной записи.

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

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

Оглавление курса