Что делает браузер после получения HTML по HTTP
Что происходит, когда HTTP прислал HTML? Что браузер дальше делает c HTML с учетом того, что она валидная?
Теория по задаче
Контекст: от HTTP до HTML
После получения ответа по HTTP определяется, как интерпретировать тело ответа: важны статус, заголовки и прежде всего медиатип, например Content-Type: text/html.
Далее байтовый поток преобразуется в поток символов (Unicode-кодпоинтов) с учетом кодировки. Для HTML применяется механизм определения кодировки: учитываются BOM, параметр charset в Content-Type, а также метатеги вида meta charset="utf-8" в начале документа.
Если HTML валидный, это означает корректность разметки относительно правил, но сам конвейер обработки не меняется: браузер все равно выполняет декодирование, потоковый разбор, построение DOM, загрузку подресурсов и рендеринг. Валидность снижает вероятность «неожиданных» исправлений разметки парсером, но не отменяет его работу.
Упрощенная цепочка на этом этапе:
HTTP bytes → decode/encoding sniffing → Unicode code points → HTML parsing
Парсинг HTML и построение DOM
HTML парсится потоково: по мере поступления символов запускается токенизация (превращение текста в токены), а затем построение дерева (добавление узлов в DOM). Итогом является объект Document и дерево узлов (элементов, текстовых узлов, комментариев и т. п.).
Парсер не «читает HTML как XML». Вместо этого применяется специализированный алгоритм с режимами вставки (insertion modes), которые зависят от контекста: одинаковый токен может обрабатываться по-разному в <head>, <body>, таблицах и т. п. Это сделано для совместимости с историческим Web и для предсказуемого результата на реальных страницах.
Даже при валидной разметке возможны технические остановки/возобновления парсинга. Например, выполнение некоторых скриптов может «вмешаться» в процесс (изменить DOM, вставить HTML), поэтому основной парсер обязан корректно синхронизировать построение дерева с выполнением JavaScript.
Упрощенная схема внутренностей:
code points → tokenizer → tokens → tree construction → DOM
Подресурсы и блокировки парсинга/рендеринга
По мере разбора HTML обнаруживаются подресурсы: стили, скрипты, изображения, шрифты, iframe и т. п. Браузер старается запрашивать их как можно раньше, чтобы сократить общее время загрузки страницы.
Некоторые ресурсы и действия могут блокировать основной парсер или откладывать визуальный вывод:
- Синхронные
<script src="...">безasync/deferобычно приостанавливают разбор HTML до загрузки и выполнения, потому что скрипт может изменить документ и продолжать парсинг «вперед» небезопасно с точки зрения корректности результата. - Стили из
<link rel="stylesheet" href="...">обычно блокируют рендеринг (показ), чтобы не показывать пользователю «голую» страницу без CSS и не провоцировать резкие визуальные скачки.
Чтобы уменьшить простои, в современных браузерах существует «спекулятивный» механизм раннего обнаружения ресурсов (preload scanner). Он сканирует HTML разметку параллельно основному парсеру и пытается заранее поставить в очередь загрузку ресурсов, даже если основной парсер временно остановлен скриптом.
Ограничения такого подхода важны для понимания поведения:
- Ресурсы, создаваемые только через JavaScript (например,
const img = new Image(); img.src = "..."), не будут обнаружены заранее, пока скрипт не выполнится. - Ресурсы, упомянутые только внутри CSS (например,
background-image: url(...)), не извлекаются из HTML-сканирования и появятся в сети позже — после загрузки и разбора CSS.
Мини-таблица типичных эффектов:
| Ситуация в документе | Что тормозится | Причина |
|---|---|---|
<script src="app.js"> без async/defer | Парсинг HTML и часто первый показ | Скрипт может менять DOM и поток разбора. |
<link rel="stylesheet" href="styles.css"> | Рендеринг (визуальный показ) | Избежание отображения без стилей и скачков оформления. |
| Основной парсер «стоит» на скрипте | Сеть продолжает работу через раннее сканирование | Ранние запросы подресурсов сокращают общий критический путь. |
Рендеринг: от DOM/CSSOM к пикселям
После (или параллельно) построения DOM загружаются и разбираются CSS-ресурсы, формируя CSSOM — внутреннюю модель правил стилей. Затем выполняется сопоставление стилей: для каждого узла DOM вычисляются итоговые значения CSS-свойств (cascade → computed values).
Далее строится render tree (дерево рендеринга): оно похоже на DOM, но учитывает визуальные особенности (например, элементы с display: none не попадают в render tree). На основе render tree выполняются:
- Layout (reflow): вычисление размеров и позиций боксов.
- Paint: превращение боксов в команды рисования и затем в пиксели.
- Compositing: сборка слоев (например, при
transform,opacity, фиксированных элементах) в финальную картинку кадра.
Важные практические последствия:
- Любые изменения DOM или CSS (в том числе из JavaScript) могут вызвать перерасчет стилей и layout, что дорого по времени и влияет на плавность.
- Длительное выполнение JavaScript на главном потоке может задерживать и обработку событий, и рендеринг, даже если сеть уже все скачала.
Упрощенная цепочка для запоминания:
HTML → DOM + CSS → CSSOM → style calculation → render tree → layout → paint → composite
Итого: после получения HTML браузер декодирует байты в символы, потоково парсит HTML в DOM, параллельно инициирует загрузку подресурсов и может приостанавливать парсинг/показ из‑за скриптов и стилей, затем строит CSSOM и render tree и выполняет layout/paint/compositing для вывода пикселей на экран.