Critical Rendering Path: как браузер отрисовывает страницу
Продвинутое3 минРедакция Innovialabs
Коротко
Critical Rendering Path — это цепочка шагов от получения HTML до первых пикселей на экране; чем меньше блокирующих CSS и JS в начале этой цепочки, тем быстрее пользователь видит страницу.
Что такое Critical Rendering Path
Critical Rendering Path (CRP) — последовательность шагов, которые браузер проходит, чтобы показать первый кадр страницы:
- Разобрать HTML и построить DOM.
- Разобрать CSS и построить CSSOM.
- Объединить их в render tree.
- Рассчитать геометрию элементов — layout.
- Нарисовать пиксели — paint.
- Собрать слои в итоговый кадр — composite.
Оптимизация CRP — это сокращение работы и ожиданий до первой отрисовки. Именно от неё зависят метрики FCP и LCP.
DOM и CSSOM
Браузер читает HTML потоково: байты превращаются в токены, токены — в узлы, узлы — в дерево DOM. DOM можно строить по частям, пока документ ещё загружается.
С CSS иначе. CSSOM нельзя использовать частично: правило в конце файла может переопределить правило в начале. Поэтому CSS блокирует отрисовку — браузер не нарисует страницу, пока не получит и не разберёт все стили, подключённые без условий.
Render tree, layout, paint и composite
- Render tree содержит только видимые узлы с вычисленными стилями. Элементы с
display: none,<head>,<script>в него не попадают. - Layout (в Firefox — reflow) вычисляет размеры и позиции каждого блока относительно viewport.
- Paint заполняет пиксели: текст, цвета, тени, изображения.
- Composite накладывает слои друг на друга с учётом порядка и прозрачности.
Важная практическая деталь: изменение width или top запускает layout заново, изменение color — только paint, а transform и opacity часто обрабатываются только на этапе composite. Поэтому анимации лучше строить на transform и opacity.
Что блокирует отрисовку
| Ресурс | Блокирует парсинг HTML | Блокирует отрисовку |
|---|---|---|
<link rel="stylesheet"> | нет | да |
<script> без атрибутов | да | косвенно |
<script defer> | нет | нет |
<script async> | только во время выполнения | нет |
<script type="module"> | нет (ведёт себя как defer) | нет |
Обычный <script> останавливает парсер: браузер скачивает и выполняет его, а если перед ним есть CSS — ещё и ждёт CSSOM, потому что скрипт может читать стили.
Как сократить путь
1. Меньше критических ресурсов. Всё, что не нужно для первого экрана, грузите позже.
2. Скрипты — с defer или async.
<script src="/app.js" defer></script>
<script src="/analytics.js" async></script>
defer сохраняет порядок и выполняется после разбора HTML, async — сразу по готовности, без порядка. Для независимой аналитики подходит async, для кода приложения — defer.
3. Критический CSS — inline. Стили первого экрана можно встроить в <style> в <head>, а остальное подгрузить отдельно.
4. Условные стили через media.
<link rel="stylesheet" href="/print.css" media="print">
Такой файл загрузится, но не будет блокировать отрисовку на экране.
5. Раньше находить важные ресурсы. <link rel="preload"> для шрифта или главного изображения, <link rel="preconnect"> для сторонних доменов.
6. Меньше байтов. Сжатие (gzip, Brotli), минификация, удаление неиспользуемого CSS.
7. Шрифты без невидимого текста. font-display: swap показывает запасной шрифт, пока загружается основной.
Типичные ошибки
- Скрипты в
<head>безdefer— страница ждёт их перед показом контента. - Один огромный CSS-бандл на весь сайт, из которого первому экрану нужна малая часть.
@importвнутри CSS: файлы загружаются цепочкой, а не параллельно.- Анимации через
top/left/width, которые вызывают layout на каждом кадре. - Чтение
offsetHeightсразу после изменения стилей в цикле — это forced synchronous layout (layout thrashing). - Главное изображение с
loading="lazy"— LCP-элемент нельзя откладывать.
Как измерить
Во вкладке Performance в Chrome DevTools видно, когда построены DOM и CSSOM, сколько длились layout и paint и какие ресурсы блокировали первый кадр. Lighthouse отдельно подсвечивает render-blocking resources. Подробно этапы описаны в документации web.dev.