Browser API
October 23, 2023

Как браузер рендерит страницу?

Общее понимание

Как только страница загрузилась, браузер преобразует полученную от сервера строку с HTML в специальную абстракцию — DOM-дерево.

Когда парсер встречает ссылки, он создает запросы для их скачивания. Процесс парсинга HTML заканчивается когда все ресурсы скачаны, а сборка DOM-дерева завершена.

Далее браузер преобразует CSS в CSS-дерево.

Когда оба дерева созданы, браузер комбинирует их в одно — Render-дерево, в нем вычисляются стили для каждого видимого элемента страницы.

Далее браузер занимается компоновкой, определяя размеры и позицию элементов в Render-дереве.

Как только компоновка завершена — страница отрисовывается на экране.

Глубокое понимание

Общее понимание можно конкретизировать и разделить на CRP этапы (critical rendering path) или этапы критические рендеринга:

  • Построение DOM-дерева
  • Построение CSSOM-дерева
  • Создание Render-дерева
  • Компоновка
  • Отрисовка

Построение DOM-дерева (Document Object Model)

Работать напрямую с HTML-разметкой довольно дорого и долго.

Поэтому сначала браузер парсит HTML, а затем создает его абстрактное представление — объектную модель документа (DOM).

Построение DOM является инкрементальным, то есть парсер последовательно преобразует весь HTML-документ в токены, из которых формирует узлы.

Токен (token)

  • Начинается со startTag, заканчивается endTag

Узел (node)

  • Содержит информацию о HTML-элементе и своих потомках
  • Состоит из токенов с определенной иерархией

Чем больше количество узлов, тем дольше происходит формирование DOM.

Внешние ресурсы

Когда парсер находит теги с ссылками на внешние ресурсы, он запрашивает их скачивание. Такие запросы могут блокировать построение DOM, например:

  • Блокируют
    • Скрипты
    • Стили
  • Не блокируют
    • Изображения
    • Шрифты

Параллельно созданию DOM-дерева происходит создание CSSOM-дерева.

Построение CSSOM-дерева (CSS Object Model)

CSSOM-дерево содержит все данные о стилях DOM-дерева.

Преобразование CSS в CSSOM проходит так, чтобы вложенные узлы наследовали стили от родительских.

Процесс построения CSSOM не инкрементальный, то есть он блокирует процесс построения CSSOM-дерева до момента когда все CSS-правила будут получены и обработаны.

Это связано возможностью перезаписать CSS-правило из любого места CSS-документа.

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

Наименее специфичные селекторы срабатывают быстрее, например:

.foo {} /* сработает быстрее */

Для поиска .foo понадобится одна операция.

.bar .foo {} 

Сначала будут найдены все .foo, а потом браузер пройдёт вверх по дереву в поисках родительского элемента .bar

Более специфичные селекторы требуют от браузера большего количества работы, но эти проблемы не стоят их оптимизации.

Создание Render-дерева (Render Tree)

Как только браузер составил DOM и CSSOM, он объединяет их в общее дерево рендеринга — Render-дерево.

Процесс объединения запускает проверку каждого DOM-узла, начиная от корневого, затем совмещает каждый элемент с его стилями из CSSOM.

display: none

Наличие свойства display: none предполагает исключение узла и его потомков из процесса создания Render-дерева.

Когда Render-дерево построено запускается процесс компоновки.

Компоновка (планировка, layout, re-flow)

На этапе компоновки высчитывается геометрия макета: определяется расположение и размеры элементов, то как они будут влиять или не влиять друг на друга.

Компоновка делится на два этапа:

Глобальная компоновка

Просчитывает каждый элемент в дереве и является очень дорогой операцией, например:

  • Изменение viewport

Инкрементальная компоновка

Просчитывает частично только «грязные» элементы, которые появляются в случае:

  • Изменения стилей box-модели элемента
  • Добавления нового узла или элемента

О viewport

viewport определяет ширину видимой области экрана, которая по умолчанию ровна 960px.

<meta name="viewport" content="width=device-width"> 

Установка width=device-width делает ширину видимой области в 100% от ширины экрана устройства.

device-width изменяется каждый раз, когда пользователь поворачивает телефон. Это приводит к запуску этапа компоновки, как и при изменении размеров окна в обычном браузере.

Производительность

На производительность компоновки влияет DOM — чем больше узлов, тем больше времени понадобится на перерасчёт позиций и размеров всех элементов.

Вызов компоновки во время скролла или анимации, может стать узким местом

Для уменьшения частоты и продолжительности этого этапа, группируйте обновления экрана и избегайте анимации свойств, связанных с box-моделью.

Отрисовка (paint, re-paint)

Когда Render-дерево создано, а компоновка произведена — браузер начинает рисовать страницу, располагая пиксели в нужные места экрана, чтобы все элементы страницы отобразились там где должны.

Отрисовка делится на два этапа:

Глобальная отрисовка

Первичная полная отрисовка всей страницы.

Инкрементальная отрисовка

Браузер делит весь viewport на прямоугольные участки. Если изменения ограничены одним участком, то он обозначается «грязным», а это приводит к его перерисовкe.

Порядок отрисовки связан со стековым контекстом. В общих чертах, отрисовка начинается с заднего плана и постепенно переходит к переднему.

Свойства вызывающие re-paint:

background-color
background-image
border
children
outline

Отрисовка является самым дорогим процессом.

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

  • Создание запроса к статическому ресурсу из HTML
  • Скорость получения ответа от сервера
  • Загрузка статического ресурса
  • Парсинг и выполнение скриптов

Для гладкой работы интерфейса процесс отрисовки должен укладываться в стандартную частоту кадров экрана — 60 fps или 16ms.

Дополнительно

Композитинг

Компоновка и отрисовка работают за счёт CPU, поэтому относительно медленные, это делает плавные анимации невероятно дорогими.

Для плавных анимаций в браузерах предусмотрен — композитинг.

Композитинг это разделение содержимого страницы на «слои», которые браузер будет перерисовывать. Эти слои друг от друга не зависят, поэтому изменение элемента в одном слое не затрагивает элементы из других слоёв, и перерисовывать их становится не нужно.

Применение таких свойств, как transform, выносит элемент на отдельный композитный слой.

Перерисовка (reflow, repaint)

Процесс отрисовки — циклический. Браузер перерисовывает экран каждый раз, когда на странице происходят какие-то изменения, например, если в DOM-дереве добавился новый узел или изменился текст.

С помощью requestAnimationFrame мы можем указать браузеру что хотим запускать анимацию в один цикл обновления — animation frame.

Оптимизация CRP

  • Уменьшайте количество критических ресурсов, откладывая их загрузку, помечая их как async и/или группируя их
  • Оптимизируйте количество необходимых запросов, а так же размеры файлов
  • Оптимизируйте порядок так, чтобы критические ресурсы загружались в первую очередь, сокращая таким образом длину критических этапов рендеринга.

Полезное: