Когда происходит рендеринг и как браузер превращает HTML и CSS в изображение
Что проверяют
Понимаете ли вы конвейер от байтов HTML до пикселей и где в нём страница становится медленной. Следом почти всегда спрашивают, чем reflow отличается от repaint и почему анимировать transform дешевле, чем top.
Как отвечать
Этапы по порядку:
- HTML в DOM: парсинг идёт потоково, по мере прихода байтов, а preload-сканер параллельно находит ресурсы и начинает их качать;
- CSS в CSSOM: CSS блокирует отрисовку, иначе мелькнёт страница без стилей. Синхронный
<script>останавливает парсер и ждёт CSS выше себя,deferиtype="module"выполняются после разбора документа,asyncсразу после загрузки; - стили: каждому видимому узлу вычисляются итоговые значения. Узлы с
display: noneв дерево отрисовки не попадают, а псевдоэлементы::before, которых нет в DOM, попадают; - layout: размеры и координаты боксов;
- paint: запись команд рисования по слоям;
- composite: слои растеризуются тайлами и собираются в кадр на отдельном потоке, обычно силами GPU.
Теперь про «когда». Рендеринг встроен в event loop: возможность обновить кадр появляется примерно раз в период обновления экрана, на 60 Гц это около 16,7 мс. Сначала отрабатывают текущая задача и все микрозадачи, потом колбэки requestAnimationFrame, затем стили, layout и paint. Если ничего не изменилось или вкладка скрыта, кадр пропускается. Пока идёт долгая задача в JS, кадр не рисуется вовсе, это напрямую бьёт по INP.
Цена изменения зависит от этапа, с которого приходится пересчитывать: width запускает layout, paint и composite, color только paint и composite, а transform и opacity на отдельном слое обходятся композицией.
Где спотыкаются
Считают DOM и дерево отрисовки одним и тем же. Разница видна на display: none, на visibility: hidden (место занимает, в дереве есть) и на псевдоэлементах.
Не знают про принудительный синхронный layout. Чтение offsetHeight или getBoundingClientRect() сразу после записи стилей заставляет пересчитать геометрию немедленно, а в цикле это layout thrashing.
Советуют will-change на всё подряд. Каждый слой занимает память видеокарты, и на слабых устройствах избыток слоёв тормозит сильнее, чем помогает.