42 Модуль 9 Качество и производительность 17 мин урок 17 из 25

Быстрая загрузка

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

Что ты научишься делать

  • Разберёшь, какие ресурсы задерживают первую отрисовку
  • Подключишь шрифты так, чтобы текст был виден сразу
  • Уберёшь сдвиг вёрстки при загрузке картинок

#Две секунды, которые решают всё

Человек не ждёт загрузки — он ждёт содержимого. Если через две секунды на экране белый лист, половина уходит, не дождавшись.

Важна не общая скорость, а момент, когда появилось первое осмысленное. Страница может догружать картинки ещё пять секунд — если текст уже виден, это никого не раздражает.

Интерактивный разбор доступен при включённом JavaScript.

#Что блокирует показ

Браузер разбирает разметку сверху вниз и по дороге натыкается на ссылки: стили, скрипты, картинки. Некоторые из них останавливают процесс до полной загрузки файла, другие — нет.

РесурсБлокирует лиПочему
<link rel="stylesheet">Да, отрисовкуБраузер не станет показывать неоформленный текст
<script> без атрибутовДа, разбор разметкиСкрипт может дописать что угодно в документ
<script defer>НетСкачивается параллельно, выполняется после разбора
<script async>МожетВыполняется сразу по готовности, в непредсказуемый момент
ШрифтПоказ текстаПо умолчанию браузер прячет текст, пока шрифт грузится
<img>НетНо без размеров вызывает сдвиг вёрстки

#Критический CSS

Файл стилей блокирует отрисовку целиком, хотя для первого экрана нужна малая его часть.

Приём: стили первого экрана встроить прямо в head, а основной файл подключить так, чтобы он не блокировал.

HTMLindex.html
<head>  <style>    /* Только то, что видно без прокрутки: шапка, герой, типографика.       Обычно укладывается в 5–10 килобайт */    body { margin: 0; font-family: system-ui, sans-serif; }    .header { display: flex; padding: 16px; }    .hero { padding: 48px 16px; }  </style>  <!-- Основной файл не блокирует: грузится как «печатный»,       а по готовности переключается на обычный -->  <link rel="stylesheet" href="main.css" media="print"        onload="this.media='all'">  <noscript><link rel="stylesheet" href="main.css"></noscript></head>

#Шрифты: самая частая потеря секунды

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

font-display: swap меняет поведение: текст рисуется запасным шрифтом сразу, а потом подменяется.

CSSfonts.css
@font-face {  font-family: "Onest";  src: url("/fonts/onest-var.woff2") format("woff2-variations");  font-weight: 100 900;  /* Текст виден сразу, шрифт подменится при готовности */  font-display: swap;  /* Только нужные символы: кириллица и латиница.     Полный набор с иероглифами весит в разы больше */  unicode-range: U+0000-00FF, U+0400-04FF;}:root {  /* Запасной шрифт подогнан по метрикам — подмена почти незаметна */  --font-text: "Onest", "Segoe UI", system-ui, sans-serif;}

При подмене шрифта текст слегка прыгает: у запасного другие пропорции. Это лечится подгонкой метрик через дескрипторы size-adjust, ascent-override и descent-override — они подгоняют системный шрифт под пропорции основного.

CSSЗапасной шрифт под метрики основного
@font-face {  font-family: "Onest Fallback";  src: local("Segoe UI"), local("Helvetica Neue");  /* Подгоняем размер, чтобы подмена не двигала строки */  size-adjust: 97%;  ascent-override: 92%;  descent-override: 24%;}:root {  --font-text: "Onest", "Onest Fallback", sans-serif;}

#preconnect и preload

Две подсказки браузеру, которые часто путают.

preconnect — «мы точно пойдём на этот домен, установи соединение заранее». Экономит сотни миллисекунд на рукопожатии.

preload — «этот конкретный файл понадобится немедленно, начни качать сейчас». Нужен для ресурсов, о которых браузер узнаёт поздно: шрифт, упомянутый внутри CSS.

HTMLВ самом верху head
<!-- Соединение с доменом шрифтов устанавливается заранее --><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><!-- Шрифт упомянут внутри CSS — браузер узнал бы о нём поздно.     crossorigin обязателен даже для своего домена --><link rel="preload" href="/fonts/onest-var.woff2"      as="font" type="font/woff2" crossorigin>

#Картинки и сдвиг вёрстки

Сдвиг вёрстки — когда содержимое прыгает при загрузке. Человек начинает читать, и текст уезжает вниз, потому что сверху догрузилась картинка.

Лечится тем, что мы разбирали в пятом уроке основ: атрибутами width и height. Браузер вычисляет пропорцию и резервирует место заранее.

HTMLПолный набор
<!-- Первый экран: грузим сразу и с высоким приоритетом --><img  src="hero.webp"  alt="Бариста наливает молоко в чашку"  width="1200" height="800"  fetchpriority="high"  decoding="async"><!-- Ниже сгиба: по мере прокрутки --><img src="gallery-3.webp" alt="Витрина"     width="600" height="400"     loading="lazy" decoding="async">

#Чем мерить

Три показателя описывают загрузку с точки зрения человека, а не сервера. Они и лежат в основе оценок поисковиков.

ПоказательЧто означаетХорошо
LCP — отрисовка главного элементаКогда появился самый крупный блок первого экрана: заголовок или картинкаДо 2,5 секунды
CLS — сдвиг вёрсткиНасколько содержимое прыгало при загрузкеМеньше 0,1
INP — отклик на действиеЧерез сколько страница ответила на нажатиеДо 200 миллисекунд

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

#Как это выглядит в реальном проекте

HTMLПорядок в head
<head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1">  <!-- 1. Соединения — самыми первыми -->  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>  <!-- 2. Критические ресурсы -->  <link rel="preload" href="/fonts/onest-var.woff2"        as="font" type="font/woff2" crossorigin>  <!-- 3. Стили первого экрана — встроены -->  <style>/* … критический CSS … */</style>  <!-- 4. Остальные стили — не блокируют -->  <link rel="stylesheet" href="/styles/main.css" media="print"        onload="this.media='all'">  <noscript><link rel="stylesheet" href="/styles/main.css"></noscript>  <!-- 5. Скрипты — с defer -->  <script type="module" src="/js/app.js" defer></script>  <title>Кофейня «Полка»</title></head>

#Когда НЕ надо

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

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

ВозможностьСостояние
font-displayВо всех современных браузерах
preconnect, preloadВо всех современных браузерах
loading="lazy"Во всех современных браузерах
fetchpriorityПоддерживается в большинстве; где нет — просто игнорируется
size-adjust и переопределение метрикВо всех современных браузерах
decoding="async"Во всех современных браузерах

Где спотыкаются почти все

Шрифт без font-display

Почему Браузер прячет текст до трёх секунд. Человек видит пустой макет вместо содержимого.

Как надо Всегда пиши font-display: swap.

preload без crossorigin у шрифта

Почему Шрифты запрашиваются в анонимном режиме. Без атрибута предзагруженный файл не совпадёт с настоящим запросом, и он скачается дважды.

Как надо У шрифтов crossorigin обязателен, даже на своём домене.

Скрипт без defer в head

Почему Разбор разметки останавливается до полной загрузки и выполнения скрипта.

Как надо Добавь defer. Для модулей он подразумевается по умолчанию.

Картинки без width и height

Почему Место не резервируется, содержимое прыгает при загрузке.

Как надо Указывай настоящие размеры файла, а масштабируй в CSS.

Первая отрисовка First Contentful Paint
Момент, когда на экране появилось первое содержимое.
Сдвиг вёрстки Cumulative Layout Shift
Насколько содержимое прыгало при загрузке.
Блокирующий ресурс render-blocking
Файл, из-за которого браузер откладывает показ страницы.
Критический CSS critical CSS
Стили первого экрана, встроенные прямо в разметку.
Невидимый текст FOIT
Период, когда текст не показан, потому что шрифт ещё грузится.

Проверь себя

  1. Что произойдёт со шрифтом без <code>font-display: swap</code>?

    Это и называется невидимым текстом. Человек видит пустой макет вместо содержимого.

  2. Чем <code>defer</code> лучше <code>async</code> для основного скрипта?

    <code>async</code> выполняется в непредсказуемый момент и может не найти нужный элемент.

  3. Почему у <code>preload</code> шрифта обязателен <code>crossorigin</code>?

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

  4. Какой картинке НЕ нужен <code>loading="lazy"</code>?

    На первом экране ленивая загрузка только задерживает появление картинки.

  5. Зачем нужен <code>&lt;noscript&gt;</code> рядом с приёмом <code>media="print"</code>?

    Приём держится на обработчике <code>onload</code>. Запасная ссылка обязательна.

Задание

Ускорить страницу

Четыре проблемы: скрипт блокирует разбор, у шрифта нет font-display, нет предварительного соединения с доменом шрифтов, а у картинки первого экрана не заданы размеры и стоит ленивая загрузка.

Почини всё во вкладке HTML.

Открыть в песочнице
    Подсказка 1

    Скрипт: <script src="/js/app.js" defer></script>

    Подсказка 2

    Соединение: <link rel="preconnect" href="https://fonts.example" crossorigin> — первой строкой.

    Подсказка 3

    В @font-face добавь строку font-display: swap;

    Подсказка 4

    Картинке верни размеры: width="1200" height="800" и убери loading="lazy" — она на первом экране.

    Показать решение
    HTMLindex.html
    <link rel="preconnect" href="https://fonts.example" crossorigin><link rel="stylesheet" href="https://fonts.example/onest.css"><script src="/js/app.js" defer></script><style>  @font-face {    font-family: "Onest";    src: url("/fonts/onest.woff2") format("woff2");    font-display: swap;  }</style><h1>Кофейня «Полка»</h1><img class="hero" src="hero.webp" alt="Бариста за стойкой"     width="1200" height="800" fetchpriority="high" decoding="async"><p>Варим кофе с восьми утра.</p>
    CSSstyle.css
    body { font-family: "Onest", system-ui, sans-serif; }.hero { width: 100%; height: auto; border-radius: 12px; }

    Проверь себя