Как браузер рендерит HTML, CSS и JavaScript

Как браузер рендерит HTML, CSS и JavaScript

Браузер получил HTML-страницу. Но HTML — это просто текст. Как он превращается в пиксели на экране? В этом уроке разберём процесс рендеринга: от получения HTML до построения DOM, CSSOM и Render Tree.

Два критически важных дерева

Браузер строит две структуры данных в памяти, без которых рендеринг невозможен:

  • DOM (Document Object Model) — дерево, представляющее HTML-структуру страницы. Каждый HTML-элемент становится узлом дерева.
  • CSSOM (CSS Object Model) — дерево, представляющее CSS-стили. Каждое CSS-правило становится узлом с вычисленными стилями.

Объединение DOM и CSSOM даёт Render Tree — дерево, которое уже можно отрисовать. Оно содержит только видимые элементы (без <head>, без display: none), но с вычисленными стилями для каждого.

Построение DOM: от байтов к дереву

Браузер получает HTML как поток байтов. Преобразование проходит стадии:

Байты → Символы → Токены → Узлы → DOM
  1. Байты в символы. Браузер смотрит Content-Type (обычно text/html; charset=utf-8) и декодирует байты в Unicode-строку.
  2. Символы в токены. HTML-парсер (tokenizer) проходит строку и выделяет токены: открывающие теги (<div>), закрывающие (</div>), текст, комментарии.
  3. Токены в узлы. Каждый токен создаёт узел DOM: элемент, текст, комментарий.
  4. Узлы в DOM-дерево. Узлы связываются в иерархию родитель–потомок согласно вложенности тегов.

HTML-парсер очень толерантен. Он прощает незакрытые теги, пропущенные кавычки, нестандартные элементы. Браузер никогда не падает с ошибкой парсинга — он применяет алгоритмы исправления и строит DOM как может:

<!-- Такой кривой HTML -->
<p>Текст <strong>жирный</p></strong>

<!-- Браузер построит: -->
<p>Текст <strong>жирный</strong></p>

Блокирующий парсинг: CSS и JavaScript

DOM строится инкрементально — браузер не ждёт полной загрузки HTML. Но есть два фактора, блокирующих построение:

  • CSS блокирует рендеринг. Браузер не начнёт отрисовку, пока не загрузит и не распарсит все <link rel="stylesheet"> в <head>. Иначе был бы «flash of unstyled content» (FOUC): пользователь видит неоформленную страницу, а потом она резко преображается.
  • JavaScript (синхронный <script> без async/defer) блокирует парсинг DOM. Когда парсер встречает <script>, он останавливается, ждёт загрузки скрипта, выполняет его, и только потом продолжает парсить HTML дальше. Это потому, что скрипт может писать document.write() и менять парсинг.
<head>
  <link rel="stylesheet" href="style.css">   <!-- блокирует рендеринг -->
  <script src="app.js"></script>              <!-- блокирует парсинг DOM -->
</head>

Решение: async и defer у скриптов, минимизация CSS в критическом пути.

Построение CSSOM

Параллельно с DOM браузер строит CSSOM. Он проходит все CSS-правила (из <link>, <style>, inline style="", user-agent stylesheet) и строит дерево стилей. В отличие от HTML-парсера, CSS-парсер строгий: невалидное правило молча пропускается.

CSSOM содержит вычисленные значения: font-size: 1.2em превращается в конкретные пиксели (например, 19.2px). Наследуемые свойства (цвет, шрифт) копируются от родителя.

Render Tree: объединение DOM и CSSOM

Имея DOM и CSSOM, браузер строит Render Tree:

DOM:
  body
    div.header
      h1 "Заголовок"
    div.content
      p "Текст"

CSSOM:
  h1 { font-size: 24px; color: black; }
  .header { background: #eee; }
  .content { width: 70%; }

Render Tree:
  body (visible)
    div.header (visible, background: #eee)
      h1 (visible, font-size: 24px, color: black)
    div.content (visible, width: 70%)
      p (visible)

Из Render Tree исключены:

  • <head> и его содержимое (не отображается).
  • Элементы с display: none (они есть в DOM, но не в Render Tree).
  • Псевдо-элементы ::before/::after — наоборот, есть в Render Tree, но не в DOM.

Каждый узел Render Tree знает свои геометрию, стили и порядок отрисовки.

Layout (reflow): вычисление геометрии

Имея Render Tree, браузер вычисляет точные координаты и размеры каждого элемента — это этап Layout (или reflow). Браузер проходит дерево сверху вниз, определяя:

  • Ширину каждого элемента (зависит от родителя, CSS-свойств, содержимого).
  • Высоту (зависит от содержимого, может требовать нескольких проходов).
  • Позицию (x, y согласно потоковому позиционированию, float, flexbox, grid).

Layout — вычислительно дорогой этап. Изменение одного элемента может вызвать reflow всего документа (например, изменение ширины окна браузера).

Paint и Compositing

После Layout'а браузер рисует пиксели:

  1. Paint. Для каждого узла Render Tree генерируются команды рисования: «нарисовать фон цвета #eee», «нарисовать текст 'Заголовок' шрифтом 24px». Это происходит на отдельных слоях.
  2. Compositing. Слои собираются вместе в финальное изображение на экране. Свойства вроде transform и opacity меняются только на этапе compositing — без повторного layout и paint. Поэтому анимации через transform работают быстро (60 fps), а через left/top — медленно (вызывают reflow).

Проверь себя

  1. Чем Render Tree отличается от DOM?
  2. Почему CSS в <head> блокирует рендеринг?
  3. Почему анимация transform: translateX(100px) быстрее, чем left: 100px?
<details> <summary>Ответы</summary>
  1. DOM содержит ВСЕ элементы HTML (включая <head>, display: none). Render Tree содержит только видимые элементы с вычисленными стилями — готовые к отрисовке.
  2. Без CSSOM нельзя построить Render Tree (неизвестны стили). Если бы браузер рендерил без CSS, пользователь видел бы «голый» HTML, а через мгновение — оформленный — это FOUC.
  3. transform работает только на этапе compositing — GPU сдвигает готовый слой, не пересчитывая геометрию. left вызывает Layout (reflow) — браузер заново вычисляет позиции всех элементов.
</details>

Что унести с урока

  • Браузер строит два дерева: DOM (из HTML) и CSSOM (из CSS).
  • Render Tree = DOM + CSSOM. Только видимые элементы с вычисленными стилями.
  • CSS блокирует рендеринг, JS блокирует парсинг DOM.
  • Этапы отрисовки: Layout (геометрия) → Paint (команды рисования) → Compositing (слои на экран).

В следующем уроке детальнее разберём Critical Rendering Path — как оптимизировать последовательность загрузки, чтобы страница показалась пользователю как можно быстрее.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике