Как браузер рисует HTML-страницу

Прежде чем HTML-код от сервера отобразится пикселами на экране браузеру предстоит проделать огромную работу, которую мы, веб-разработчики, обычно не замечаем. Мы не знаем, как разметка, стили и скрипты превращаются в страницы на экране. Если коротко, то этот процесс состоит из следующих этапов:

  • браузер загружает HTML документ;
  • начинается его синтаксический анализ (парсинг);
  • во время парсинга браузер строит модели DOM и CSSOM;
  • далее движок браузера комбинирует эти две объектные модели, чтобы создать дерево визуализации или по другому дерево рендера (Render Tree);
  • затем запускается процесс компоновки (Layout);
  • и в конце запускается процесс рендеринга или по другому процесс отрисовки страницы на экрана (Paint).

Последовательность этих шагов, необходимая для первого отображения страницы на экране, называется "Критический путь рендеринга" (Critical Rendering Path). Понимание его этапов поможет разобраться как улучшить производительность сайта или приложения.

Загрузка документа и построение модели DOM

1. Когда мы открываем ссылку в браузере, он обращается к запрашиваемому ресурсу и в ответ получает от него набор необработанных байт, а не символы написанного вами кода. Браузер получил байты, но пока не может ничего с ними сделать, так как они должны быть преобразованы в понятную ему форму.

2. На втором шаге браузер преобразует полученные байты в символы, основываясь на приведенной в файле кодировке (например, UTF-8).

3. Затем на основании стандарта W3C HTML5 браузер выделяет среди символов теги в угловых скобках и преобразует их в токены. Этот процесс называется токенизация (Tokenizing). Концептуально можно рассматривать токен как своего рода структуру данных, содержащую информацию об определенном HTML-теге. По сути, HTML-файл разбивается на небольшие единицы синтаксического анализа, называемые токенами. Именно так браузер начинает понимать, что вы написали.

4. Далее токены преобразуются в узлы. Это обычные JavaScript-объекты с определенными свойствами. Узел - это отдельная сущность в дереве объектов всего документа.

5. Узлы собираются в древовидную структуру, повторяющую иерархию HTML-файла, в котором одни теги помещаются в другие. Именно эта структура и называется DOM (Document Object Model) - объектная модель документа или дерево DOM (DOM Tree).

DOM - это внутреннее представление HTML-страницы браузером, а также набор объектов и API, с которыми веб-разработчик может взаимодействовать с помощью JavaScript.

Не смотря на то, что дерево DOM похоже на исходный HTML-документ, все же оно им не является. Основные отличия:

  • невалидный HTML всегда будет преобразован в валидную объектную модель;
  • с помощью JavaScript DOM можно изменить, а исходный HTML останется прежним.

Как выглядит DOM можно наглядно посмотреть в онлайн-инструменте Live DOM Viewer. Просто пишем в верхнем окошке произвольный HTML и смотрим как он преобразуется в DOM.

Построение модели CSSOM

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

Чтобы обработать данные из CSS-файла, браузер должен выполнить те же самые действия, что и с HTML-документом:

Преобразовать байты в символы, символы в токены, токены в объекты, а из объектов построить дерево CSSOM.

CSSOM (CSS Object Model) - объектная модель CSS - это набор объектов, представляющий стили, связанные с DOM. Он выглядит так же как DOM, но с соответствующими стилями для каждого узла.

Несмотря на то, что CSSOM похожа на DOM, отличия все жесть. Например, CSSOM не будет содержать объектов, которые нельзя распечатать на экране, таких как <meta>, <script>, <title> и т.д.

Создание дерева визуализации (Render Tree)

На этом шаге мы имеем две независимые структуры DOM и CSSOM. DOM содержит всю информацию об отношениях HTML-элементов страницы, в то время как CSSOM содержит информацию о том, как стилизованы элементы.

Теперь эти две структуры объединяются в одну под названием "дерево визуализации". Для построения этого дерева браузер проверяет каждый узел DOM, начиная от корневого и определяет, какие CSS правила нужно присоединить к этому узлу.

Дерево визуализации охватывает только видимое содержимое. Например, секция head не содержит никакой видимой информации, а потому может не включаться в дерево. Кроме того, если у какого-то узла стоит свойство display: none, оно так же не включается в дерево (как и потомки этого узла).

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

Процесс компоновки (Layout)

Браузер уже определил, какие объекты будут видны на странице и какие стили нужно им присвоить. Пришло время создать макет, т. е. выяснить, какого размера будут объекты и как их нужно расположить в области просмотра.

Браузер создает макет каждого отдельного узла дерева визуализации. Макет состоит из размера узла в пикселях и места (положения), в котором он будет напечатан на экране.

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

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

По возможности надо стараться избегать лишних операций компоновки, так это весьма ресурсоемкий процесс. Вот статья Пола Льюиса, в которой он рассказывает о том, как мы можем избежать сложных и дорогостоящих операций по компоновке.

Отрисовка страницы (Paint)

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

Предварительное сканирование или спекулятивный парсинг

Так как скрипты на время своей загрузки и выполнения блокируют загрузку других ресурсов страницы (таких как картинки, стили, другие скрипты), то до 2008 года все внешние ресурсы загружались браузером последовательно. При обнаружении внешнего скрипта он загружался и выполнялся, затем загружался и выполнялся следующий внешний скрипт и так далее.

Это изменилось около 2008 года, когда IE ввели что-то, что они называли "смотрящим вперед загрузчиком". Это был способ продолжить загрузку файлов, которые были необходимы, в то время как выполнялся текущий скрипт. За ними последовали Firefox, Chrome и Safari, и сегодня большинство браузеров используют эту технику под разными именами. Chrome и Safari называют ее "предварительный сканер", а Firefox - "спекулятивный парсер".

Это способ оптимизации обработки документа. Суть заключается в том, что парсер одновременно с синтаксическим анализом производит ряд действий вспомогательного характера, результат которых не влияет напрямую на основной процесс. То есть пока выполняется код скриптов, с помощью других потоков браузер обрабатывает остальные части документа, определяет связанные с ним ресурсы и производит их загрузку. Это позволяет загружать ресурсы с использованием дополнительных параллельных соединений, что в результате снижает время, требуемое на обработку документа. Однако, имейте в виду, что все операции, связанные со спекулятивным парсингом никоим образом не влияют на структуру DOM дерева. То есть построение и модификация синтаксического дерева производится исключительно основным процессом парсинга.

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

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

  • скрипты
  • внешние файлы CSS
  • изображения из тегов <img>