Что блокирует рендеринг страницы в браузере

Что в браузере блокирует рендеринг страницы?

Теория: критический путь рендера

Критический путь рендера (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
Даже при полностью загруженных HTML и CSS отрисовка может задерживаться из‑за «длинных задач» на main thread: пока выполняется тяжёлая функция JavaScript, браузер не может одновременно делать 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>Обычно не блокирует первый кадр целикомРисуется по готовности, каркас может появиться раньше
Смысл «критического пути» в том, что для первого кадра важнее всего быстро получить HTML для DOM, быстро получить критические CSS для CSSOM и не занять main thread тяжёлыми синхронными задачами.

Практика: как проверить на странице

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

Что следует проверять:

  • 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>
Если в head расположен тяжёлый синхронный скрипт, то даже маленькая страница может долго оставаться без первой отрисовки, потому что парсер HTML остановится и будет ожидать выполнение JavaScript.

Кратко: рендеринг чаще всего блокируют CSS (для построения CSSOM), синхронные скрипты без async/defer и длительные задачи на main thread; изображения обычно не блокируют первый кадр целиком, а шрифты влияют главным образом на момент появления текста и зависят от настроек вроде font-display.