Глубокое погружение в конвейер рендеринга браузера: Полный путь от DOM до пикселей и оптимизация производительности

前端工程

Полный конвейер рендеринга

Когда браузер получает HTML, он проходит через следующие этапы для отрисовки содержимого на экране:

HTML → Парсинг → DOM-дерево
CSS → Парсинг → CSSOM-дерево
                    ↘
DOM + CSSOM → Дерево рендеринга → Layout → Paint → Compositing → Пиксели

Каждый этап имеет чётко определённые входы и выходы — понимание этих границ является основой оптимизации производительности.


Этап 1: Парсинг

Парсинг HTML

Поток байтов → Символы → Токены → Узлы → DOM

Ключевые характеристики:

  • Инкрементальный парсинг: HTML парсится как поток, без ожидания полной загрузки
  • Блокировка скриптами: <script> приостанавливает парсинг (если не async/defer)
  • Предварительное сканирование: Браузер предварительно сканирует последующие теги <link> и <script> для ранних загрузок
<!-- ❌ Блокирует парсинг -->
<script src="app.js"></script>

<!-- ✅ Не блокирует парсинг -->
<script src="app.js" defer></script>
<script src="analytics.js" async></script>

Парсинг CSS

Парсинг CSS не блокирует построение DOM, но блокирует рендеринг — браузер не будет рендерить страницу с неопределёнными стилями.

<!-- Критический CSS встроенный -->
<style>
  .above-fold { /* Стили видимой области */ }
</style>

<!-- Асинхронная загрузка некритического CSS -->
<link rel="preload" href="rest.css" as="style"
      onload="this.rel='stylesheet'">

Этап 2: Вычисление стилей

Сопоставление CSS-селекторов с DOM-элементами для вычисления итоговых вычисленных значений стилей каждого элемента.

Производительность сопоставления селекторов

/* ✅ Быстро: Сопоставление справа налево, ID находит напрямую */
#nav .item { }

/* ❌ Медленно: Универсальный селектор требует обхода всех элементов */
* .item { }

/* ❌ Медленно: Смежные селекторы могут вызвать возврат */
div > p + p { }

/* ✅ Быстро: BEM с одним именем класса */
.nav__item { }

Сложность вычисления стилей

Операция Сложность Описание
Селектор по одному классу O(1) Прямой поиск по хеш-таблице
Селектор потомка O(n) Требует обхода вверх
Универсальный селектор O(n) Обходит все элементы
:nth-child() O(n) Требует вычисления позиции

Этап 3: Layout

Вычисляет позицию и размер каждого элемента, генерируя дерево layout.

Операции, запускающие Reflow

Операция Область воздействия
Изменение width/height Текущий элемент и потомки
Изменение margin/padding Текущий элемент и последующие родственники
Изменение font-size Текущий элемент и все потомки
Изменение display Текущий элемент и все наследники
Чтение offsetWidth и т.д. Принудительный синхронный layout
window.getComputedStyle() Принудительный синхронный layout

Ловушка принудительного синхронного layout

// ❌ Перемежённое чтение/запись — каждое чтение запускает reflow
elements.forEach(el => {
  const height = el.offsetHeight; // Чтение → запускает reflow
  el.style.height = height + 10 + 'px'; // Запись → помечает как грязный
});

// ✅ Пакетное разделение чтения/записи
const heights = elements.map(el => el.offsetHeight); // Пакетное чтение
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px'; // Пакетная запись
});

Использование паттерна FastDOM

class FastDOM {
  private reads: (() => void)[] = [];
  private writes: (() => void)[] = [];

  measure(fn: () => void) { this.reads.push(fn); }
  mutate(fn: () => void) { this.writes.push(fn); }

  flush() {
    this.reads.forEach(fn => fn());  // Сначала пакетное чтение
    this.writes.forEach(fn => fn()); // Затем пакетная запись
    this.reads = [];
    this.writes = [];
  }
}

Этап 4: Paint

Растеризация элементов дерева layout в пиксели. Paint происходит слой за слоем.

Операции, запускающие Repaint

Операция Reflow? Repaint?
Изменение color
Изменение background
Изменение visibility
Изменение box-shadow
Изменение outline
Изменение opacity ❌ (композитный слой)
Изменение transform ❌ (композитный слой)

Уменьшение области Paint

/* ❌ Изменение любого свойства может вызвать repaint всего слоя */
.card {
  background: white;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

/* ✅ Повышение анимированных элементов до независимых композитных слоёв */
.animated-element {
  will-change: transform;
  /* или */
  transform: translateZ(0);
}

Этап 5: Compositing

Комбинирование нескольких окрашенных слоёв в финальное изображение. Это задача GPU.

Условия повышения до композитного слоя

Условие Пример
3D-трансформация transform: translateZ(0)
will-change will-change: transform, opacity
<video> Видеоэлементы повышаются автоматически
<canvas> Canvas 2D/WebGL
CSS-анимация/переход Анимации opacity/transform
position: fixed Элементы с фиксированным позиционированием
filter Размытие, яркость и другие фильтры

Принципы GPU-ускорения

Путь рендеринга CPU:
  Изменение стиля JS → Reflow → Repaint → Compositing → Отображение
  Время: 16-100ms

Путь рендеринга GPU (композитные слои):
  Изменение JS transform/opacity → Compositing → Отображение
  Время: 1-2ms (пропускает reflow и repaint)

Правильное использование will-change

/* ❌ Злоупотребление: Повышать каждый элемент, расходует память GPU */
* { will-change: transform; }

/* ✅ По необходимости: Повышать только перед анимацией */
.card {
  transition: transform 0.3s;
}

.card:hover {
  will-change: transform; /* Повышать только при наведении */
}

/* ✅ Динамическое управление через JS */
element.addEventListener('mouseenter', () => {
  element.style.willChange = 'transform';
});

element.addEventListener('animationend', () => {
  element.style.willChange = 'auto'; // Освободить после окончания анимации
});

Анализ рендеринга с DevTools

1. Панель Performance

Ключевые метрики:
- Зелёные полосы: Время Paint
- Фиолетовые полосы: Время Layout
- Оранжевые полосы: Время Compositing
- Красные треугольники: Длинные кадры (>16.67ms)

2. Панель Rendering

Включённые опции:
☑ Paint flashing    → Зелёная подсветка отмечает repainted области
☑ Layout Shift Regions → Синие отметки показывают смещения layout
☑ Layer borders     → Оранжевые границы отмечают композитные слои
☑ FPS meter         → Мониторинг частоты кадров в реальном времени

3. Панель Layers

Просмотр списка композитных слоёв и использования памяти:

Слой #1 (корень)     → 1200x800 → 3.8MB
Слой #2 (видео)      → 640x360  → 0.9MB
Слой #3 (модальное)  → 400x300  → 0.5MB
Итого: 5.2MB памяти GPU

Чеклист оптимизации производительности

Избегать Reflow

  1. ✅ Использовать transform вместо анимаций top/left
  2. ✅ Группировать DOM-изменения (DocumentFragment / cloneNode)
  3. ✅ Разделять чтение и запись (паттерн FastDOM)
  4. ✅ Устанавливать display:none для внеэкранных элементов перед изменением

Избегать Repaint

  1. ✅ Использовать только transform и opacity для анимаций
  2. ✅ Использовать CSS-свойство contain для ограничения области воздействия
  3. ✅ Избегать крупноформатных box-shadow и filter

Использовать Compositing

  1. will-change для повышения композитных слоёв по необходимости
  2. ✅ Повышать фиксированные элементы (header/footer) до независимых слоёв
  3. ✅ Мониторить память GPU для предотвращения взрыва слоёв

CSS-сдерживание

/* Ограничить область воздействия стилей/layout/paint */
.widget {
  contain: layout paint style;
}

/* Строгое сдерживание: содержимое не влияет на внешнее */
.isolated-component {
  contain: strict;
}

/* Сдерживание размера содержимого: подходит для элементов списка */
.list-item {
  contain: content;
}
Значение contain Предотвращает распространение Reflow Предотвращает распространение Repaint Может содержать внеэкранное содержимое
none
layout
paint
strict
content

Заключение

Понимание конвейера рендеринга браузера — основа оптимизации производительности фронтенда. Ключевой принцип: держать изменения на самой ранней возможной стадии — если проблему можно решить только на этапе compositing, никогда не запускайте reflow. Запомните три ключевых числа: reflow занимает 10-100ms, repaint занимает 1-10ms, compositing занимает 0.1-1ms. Используйте transform и opacity для анимаций, contain для ограничения области воздействия и will-change для повышения композитных слоёв по необходимости — это три столпа оптимизации производительности рендеринга.


Попробуйте эти локальные браузерные инструменты — регистрация не требуется →

#浏览器渲染#性能优化#重排#重绘#GPU加速