Что блокирует рендеринг страницы в браузере
Что в браузере блокирует рендеринг страницы?
Теория: критический путь рендера
Критический путь рендера (Critical Rendering Path) — это цепочка шагов, которая превращает HTML/CSS/JS в пиксели на экране: построение DOM, построение CSSOM, сборка render tree, затем layout (расчёт геометрии) и paint (отрисовка).
DOM строится постепенно, по мере того как парсер читает HTML, поэтому не требуется ждать «весь документ целиком», чтобы начать построение структуры. CSSOM устроен иначе: чтобы корректно вычислить итоговые стили, часто необходимо иметь уже загруженные и разобранные критические CSS, иначе возможна «вспышка» неправильного оформления.
Упрощённая схема, где видны типовые места блокировок:
HTML bytes
↓ (tokenize/parse)
DOM (строится постепенно) ------------------------------┐
├→ Render Tree → Layout → Paint (пиксели)
CSS (скачивание+парсинг) → CSSOM (нужен для стилей) ----┘
JS (classic, синхронный) может:
- остановить парсинг HTML
- занять main thread и отложить layout/paint
Что именно блокирует рендер
Чаще всего рендеринг задерживают ресурсы критического пути: таблицы стилей (нужны для CSSOM), синхронные скрипты без async/defer (могут остановить парсер и вмешаться в построение DOM), а также длительное выполнение кода, мешающее layout/paint.
Ниже — последовательное объяснение по категориям.
CSS (таблицы стилей)
- По умолчанию
<link rel="stylesheet" ...>является блокирующим для первой отрисовки, потому что без CSSOM нельзя корректно вычислить стили элементов и собрать render tree. - Чем больше критических CSS подключено в начале документа, тем позже появляется первый «правильный» кадр.
Пример (обычное блокирующее подключение стилей):
<link rel="stylesheet" href="/styles.css">
JavaScript (синхронные скрипты)
- Classic script без
async/deferобычно останавливает парсинг HTML: браузеру требуется дождаться загрузки файла и выполнить его «сейчас», прежде чем идти дальше по разметке. - Такой скрипт способен задержать и рендер, потому что меняет DOM/CSSOM и занимает main thread.
Пример (парсер-блокирующий скрипт):
<script src="/heavy.js"></script>
Пример (не останавливать парсер, выполнить после разбора HTML):
<script src="/app.js" defer></script>
Пояснение по атрибутам:
async: файл загружается параллельно, выполнение происходит сразу после готовности файла; порядок относительно HTML может быть непредсказуемым, а выполнение всё равно занимает main thread.defer: файл загружается параллельно, выполнение происходит после завершения парсинга HTML; порядок нескольких defer-скриптов сохраняется.
Main thread (длинные задачи)
- Блокировка бывает не только «сетевой» (пока ресурс скачивается), но и «вычислительной»: если JavaScript выполняется долго, то браузер не успевает рассчитать layout и выполнить paint.
- В результате интерфейс может долго не обновляться даже при загруженных ресурсах.
Шрифты (webfonts)
- Шрифты могут задерживать появление текста: иногда текст скрывается до загрузки шрифта (FOIT), иногда сначала показывается запасной шрифт, а затем происходит замена (FOUT).
- Управление поведением задаётся через
font-displayв@font-face, поэтому утверждение «страница всегда ждёт все шрифты целиком» является некорректным.
Пример (стратегия отображения текста через font-display):
@font-face {
font-family: "MyFont";
src: url("/fonts/myfont.woff2") format("woff2");
font-display: swap;
}
Изображения и медиа
- Изображения обычно загружаются асинхронно и дорисовываются, когда готовы; это важно для полноты контента и визуального качества, но обычно не является главным «стоп‑фактором» первой отрисовки всей страницы.
- Возможна ситуация, когда каркас страницы уже виден, а изображения ещё нет.
Пример (картинка может дорисоваться позже):
<img src="/hero.jpg" alt="Hero">
Таблица «ресурс → эффект»
- Для начинающего уровня полезно запомнить: CSS и синхронный JS — главные кандидаты на блокировку первой отрисовки, шрифты — частый источник проблем с текстом, длинные задачи — частая причина «пустого» или «замершего» экрана.
| Что встречено в HTML | Что задерживается | Причина (упрощённо) |
|---|---|---|
<link rel="stylesheet" href="..."> | Первая корректная отрисовка | Нужен CSSOM для вычисления стилей и сборки render tree |
<script src="..."></script> (без async/defer) | Парсинг HTML и часто рендер | Скрипт выполняется синхронно, парсер ждёт загрузку и выполнение |
<script async src="..."> | Может «сдвинуть» рендер по времени | Выполнение происходит при готовности, может занять main thread |
<script defer src="..."> | Рендер обычно менее задерживается | Парсинг не блокируется, выполнение после парсинга HTML |
@font-face webfonts | Появление текста/перерисовка | Стратегия зависит от font-display (FOIT/FOUT) |
<img> | Обычно не блокирует первый кадр целиком | Рисуется по готовности, каркас может появиться раньше |
Практика: как проверить на странице
Для диагностики обычно достаточно инструментов разработчика браузера.
Что следует проверять:
- Network: ранние запросы в head; особенно CSS и скрипты без
defer/async, затем сопоставление с водопадом загрузки. - Performance: наличие длинных задач на main thread до первого отображения; часто именно они объясняют задержку layout/paint.
- Fonts: наличие FOIT/FOUT и значение
font-displayв стилях.
Пример базового head (упрощённо: CSS — критичен, JS — отложен):
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/styles.css">
<script src="/app.js" defer></script>
</head>
Кратко: рендеринг чаще всего блокируют CSS (для построения CSSOM), синхронные скрипты без async/defer и длительные задачи на main thread; изображения обычно не блокируют первый кадр целиком, а шрифты влияют главным образом на момент появления текста и зависят от настроек вроде font-display.