Critical Rendering Path: как браузер рисует первый кадр
Что происходит между «получил HTML» и «пользователь видит контент». Пошаговый разбор и где обычно теряется время.
Между моментом «браузер получил первый байт HTML» и моментом «пользователь видит контент» проходит много невидимой работы. Понимание этой цепочки — Critical Rendering Path, CRP — отличает SEO-специалиста, который оптимизирует Core Web Vitals наугад, от того, кто знает где именно искать тормоза.
В этой статье — пошаговый разбор CRP и практические выводы для каждого этапа.
Этапы CRP
Условно их пять:
- Construct the DOM — построение дерева DOM из HTML
- Construct the CSSOM — построение дерева стилей из CSS
- Render Tree — объединение DOM и CSSOM
- Layout — вычисление геометрии (позиции, размеры элементов)
- Paint + Composite — отрисовка пикселей на экране
JavaScript встраивается между этапами и может всё сломать. Разберём по порядку.
Этап 1: DOM construction
Браузер получает HTML побайтово. Парсер построчно превращает теги в узлы DOM-дерева.
Что блокирует:
- Встретил
<script>безasync/defer— останавливает парсинг, скачивает JS, выполняет, возвращается - Встретил
<link rel="stylesheet">— продолжает парсить, но дальше не сможет начать рендер без CSSOM - Встретил
<iframe>— открывает отдельный child contexte, не блокирует основной
Что ускорить:
deferилиasyncна скриптах (см. render-blocking ресурсы)- Стримминг HTML с сервера (отдавать HTML по мере готовности, не дожидаясь полной страницы)
- Уменьшение размера HTML (gzip/brotli компрессия)
Типичный HTML-документ в 50-100 КБ парсится за 10-30 мс. Это редко узкое место. Узкое место обычно — скачивание HTML по сети.
Этап 2: CSSOM construction
CSS блокирует рендер. Браузер обязан построить полный CSSOM перед первой отрисовкой.
Как это работает:
- Браузер встречает
<link rel="stylesheet" href="main.css">в HTML - Отдельным запросом качает
main.css - Парсит CSS, строит дерево правил
- Если в CSS есть
@import— рекурсивно повторяет (медленно, см. @import в CSS)
Что блокирует:
- Большие CSS-файлы (200+ КБ некомпрессированных)
- Цепочки
@import - CSS с внешних доменов (требует DNS, handshake)
- CSS, заблокированный медленным сервером
Что ускорить:
- Inline critical CSS в HTML — убирает блокировку для above-the-fold
- Preload основного CSS —
<link rel="preload" href="main.css" as="style"> - Brotli-сжатие
- Удаление неиспользуемого CSS (PurgeCSS)
Этап 3: Render Tree
Когда DOM и CSSOM готовы, браузер строит Render Tree — это DOM, к каждому узлу которого применены вычисленные стили. Скрытые элементы (display: none) в Render Tree не попадают.
Что блокирует:
- DOM ещё не готов (тяжёлый HTML, синхронные скрипты)
- CSSOM ещё не готов (большой CSS, @import)
Этап быстрый сам по себе (5-20 мс), просто ждёт DOM + CSSOM.
Этап 4: Layout (Reflow)
Браузер вычисляет геометрию каждого узла — где он будет на экране, какого размера, как соотносится с соседями.
Что замедляет:
- Тысячи узлов в DOM (страница с 50 000 элементов будет считаться долго)
- Сложные flexbox/grid с пересчётом
- Изменение размера viewport (resize event)
- JavaScript, который меняет геометрию между paint'ами (например, добавляет элементы динамически)
Что ускорить:
- Минимизировать DOM — простая структура, без лишних обёрток
- Использовать
contain: layout styleна изолированных компонентах — браузер не будет пересчитывать всё, если меняется один блок - Не делать «forced layouts» в JS — это когда вы меняете элемент, тут же читаете его геометрию (
offsetWidth,clientHeight). Это заставляет браузер пересчитывать заново.
Группируйте чтение и запись:
// Плохо — N reflows
elements.forEach(el => {
el.style.width = el.offsetWidth + 10 + 'px'; // read+write на каждом элементе
});
// Хорошо — 1 reflow
const widths = elements.map(el => el.offsetWidth); // все reads
elements.forEach((el, i) => {
el.style.width = widths[i] + 10 + 'px'; // все writes
});
Этап 5: Paint + Composite
Финальный шаг — собственно отрисовка пикселей. Браузер раскладывает элементы по слоям, рисует каждый слой отдельно, потом компонует в финальный кадр.
Что замедляет:
- Большие области с тяжёлыми стилями (тени, фильтры, градиенты)
- Анимации
width,height,top,left— требуют переотрисовки геометрии backdrop-filter— самый дорогой эффект в современном CSS
Что ускорить:
- Анимации только через
transformиopacity— они работают на GPU, не требуют paint will-change: transformна анимируемых элементах — даёт hint браузеру выделить отдельный compositor layer- Реже использовать
backdrop-filter(если очень нужно — на маленьких областях) - Использовать
content-visibility: autoдля off-screen блоков — пропустит paint того, что не видно
Где JavaScript встраивается
JS может встроиться в любой момент CRP и всё ломать.
Синхронный JS в <head>:
Блокирует парсинг DOM. Парсер останавливается, JS скачивается, парсится, выполняется. Только потом парсер продолжает.
<head>
<script src="heavy.js"></script> <!-- блокирует, плохо -->
<script src="heavy.js" defer></script> <!-- ожидает DOM, потом выполняется -->
<script src="heavy.js" async></script> <!-- выполняется как готов, может прерывать -->
</head>
JS, манипулирующий DOM:
Если JS добавляет элементы — Layout пересчитывается. Если меняет стили — Paint пересчитывается. Минимизируйте JS-манипуляции DOM, особенно во время загрузки.
JS, измеряющий геометрию:
element.offsetWidth, getBoundingClientRect() — это «sync layout». Заставляет браузер немедленно пересчитать Layout. Если делать много раз — большая нагрузка.
Метрики и где смотреть
В Chrome DevTools → Performance:
- Запишите загрузку страницы (3-5 секунд)
- Откройте Main thread timeline
- Цветовая разметка покажет:
- Жёлтый (Scripting) — JS
- Фиолетовый (Rendering) — Layout
- Зелёный (Painting) — Paint
- Синий (Loading) — сеть
Где какой цвет занимает много места — там узкое место.
Long Tasks (>50 мс) маркируются красным треугольником в timeline. Это блокирует main thread, ухудшает INP и LCP.
Связь с Core Web Vitals
CRP напрямую влияет на все три метрики:
- LCP — это момент когда главный визуальный элемент завершил Layout + Paint. Чем быстрее CRP — тем лучше LCP.
- CLS — если Layout пересчитывается после первого Paint, элементы могут «прыгать», CLS растёт.
- INP — если JS-handler заставляет браузер делать Layout/Paint больше одного кадра — INP падает.
Подробнее — в Core Web Vitals 2026.
Резюме
Critical Rendering Path — это не теоретическая концепция, а конкретная цепочка операций, которую можно ускорить пошагово:
- Стримминг + compression HTML
- Inline critical CSS + preload остального
- Defer/async JS
- Минимизация DOM
- GPU-only animations (transform/opacity)
Понимание CRP позволяет оптимизировать целенаправленно, а не «по списку из PageSpeed Insights».
Хотите детальный разбор CRP вашего сайта? Напишите нам. Бесплатно за 2 рабочих дня.