Reflow, Repaint, Composite — что это и как это работает?
Итак, Reflow, Repaint и Composition — что же это такое? Большинство фронтенд разработчиков рано или поздно задумываются о том, как же работают веб-браузеры, каким образом происходит отрисовка страницы, что происходит при наведении мышки на элементы и прокликивания кнопок на странице. Reflow, Repaint и Composition, являются основными этапами отвечающими за отрисовку страницы в веб-браузере. Понимание как они работают и, что вызывает срабатывание того или иного процесса, позволит создавать более быстрые и эффективные веб-приложения.
Но перед тем как мы приступим к разбору этих этапов, давайте пробежимся по тем процессам, которые происходят до начала отрисовки страницы, после получения html страницы с сервера:
- Html парсится и разбивается на токены;
- Токены преобразуются в Ноды;
- Ноды собираются в DOM (Document Object Model) дерево;
- Параллельно с этим процессом происходит парсинг CSS правил и строится CSSOM (CSS Object Mode );
- CSSOM и DOM объединяются в Render Tree, похожее на DOM дерево, в котором скрываются не видимые элементы (
meta
,script
,link и display: none
) и добавляются элементы которых нет в DOM (псевдоэлементы :before, :after); - Далее начинаются процессы, которым посвящена данная статья: Reflow, Repaint и Composition.
Reflow / Layout
Итак, после того как браузер определил структуру документа и стили для каждой ноды, начинается Reflow. Хотя правильнее будет сказать Layout для первой операции, а все дальнейшие повторные операции, будет правильнее называть Reflow. Иногда этот процесс называется просто Layout, во всех случаях.
Что же происходит на этом этапе? Для того чтобы отобразить элементы на странице, браузеру необходимо понимать какого размера эти элементы и где они располагаются. Браузер проходит по рендер дереву (Render Tree), начиная от корневого элемента дерева и определяет координаты каждого элемента и пространство которое он занимает. Таким образом создается Layout Tree. Для элементов у которых display: none, вычисления не будут производиться, тем не менее элементы с visibility: hidden будут участвовать в Layout/Reflow, так же как и псевдоэлементы, у которых имеется заполненное свойство content (div::before{content:”Hello”}), хотя их нет в DOM. Определение размеров и местоположений элементов происходит не за один проход по дереву, проход может происходить несколько раз, если элементы встречающиеся позже, влияют на предыдущие элементы. В целом это очень сложная процедура, требующая значительных вычислительных ресурсов.
Каждый раз когда вы меняете свойство стиля, которое отвечает за положение и размеры элемента, происходит повторный процесс расчета размеров и положений у всех элементов, на которые могут повлиять эти изменения, происходит Reflow. Reflow’у могут быть подвергнуты как отдельные ветки Рендер дерева (Render Tree), так и все дерево целиком. Чем глубже вложенность элемента, тем больше элементов будут затронуты при Reflow, поэтому необходимо сохранять структуру вашего документа как можно более плоской. Так же для уменьшения количества Reflow операций, следует уменьшить количество CSS правил и избавиться от правил которые вы не используете.
Ниже список наиболее популярных свойств, изменение которых вызывает Reflow:
Более полный список http://goo.gl/lPVJY6 .
Так же Reflow часто срабатывает когда вы пытаетесь получить метрики элемента с помощью JS (elem.getBoundingClientRect(), elem.offsetLeft, elem.clientLeft и т.д.
), производите скроллинг или запускаете выполнение события. Здесь вы можете ознакомится более подробно какие операции в JS вызывают Reflow: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
Layout / Reflow происходит в основном потоке браузера, то есть там же где исполняется JS, крутится Event Loop и т. д. Поэтому в моменты когда у вас исполняется тяжелый код в JS, Reflow будет блокирован, как следствие интерактивность вашей страницы также будет заблокирована.
В этом видео визуализирован Reflow:
Repaint
Следующий этап после Layout/Reflow называется Paint или Repaint для последующих повторных операций. Размера и положения элементов не достаточно для того чтобы отобразить страницу, нужно знать каким образом «покрасить» эти элементы. На этапе Paint/Repaint браузер обходит Layout Tree и создает записи о том как будут отрисованы элементы на странице(позиция x,y, ширина, высота, цвет).
Так же как Layout/Reflow — это ресурсоемкий процесс, поэтому для хорошей отзывчивости вашей страницы, необходимо свести к минимуму операции которые вызывают Repaint. Вызвать Repaint могут изменения свойств color, background, visibility и подобных, в общем свойств которые не изменяют размеров и положения элемента, более полный список можно просмотреть в этой таблице http://goo.gl/lPVJY6 . Если какие либо из этих свойств изменяются при анимации, тогда происходит Repaint элементов которые были затронуты и слои (об этом подробнее в разделе о Composite) к которым принадлежат эти элементы обрабатываются GPU. Также необходимо помнить, что каждый раз когда вызывается Reflow, за ним вероятнее всего последует Repaint элемента. То есть если вы измените элементу свойство width, произойдет Reflow, а затем и Repaint затронутых элементов.
Composite
После того как браузер знает размеры, позицию и то в какой последовательности красить элементы, наступает пора конечной отрисовки элементов на странице. Для этого браузер на этапе Composite группирует различные элементы по слоям, растрирует эти слои, то есть отрисовывает пиксели и затем объединяет эти слои в готовую страницу в отдельном потоке композитора (compositor thread ). Все это делается для повышения производительности страницы. Теперь при скролле страницы достаточно просто сдвинуть в необходимый отрисованный слой и заново объединить слои в потоке композитора.
Так как размеры слоев могут быть достаточно большими, поток композитора также разделяет их на части (Tiles) и отрисовывает в первую очередь те части, которые видны в данный момент в окне браузера.
Именно благодаря этому этапу мы получаем плавную анимацию элементов при использовании таких свойств как transform, opacity.
Элементы у которых происходит анимация с использованием transform или opacity выносятся на отдельный слой. Так же можно задать элементу свойство will-change, этим свойством мы говорим браузеру о том, что мы планируем в дальнейшем анимировать этот элемент, таким образом он заранее выносится на отдельный слой. Раньше для этого использовался хак transform:translateZ(0) или transform:translate3d(0,0,0).
Так как этап Composite происходит в отдельном потоке композитора, а не в основном, то вычисления в JS никак не влияют на него. Даже если вся страница подвиснет из-за бесконечного цикла в JS, анимация которая реализована с помощью transform, opacity продолжит свое выполнение.
Благодаря тому что элементы расположены на отдельных слоях, Reflow и Repaint для элементов одного слоя не затрагивают элементы на остальных слоях, но бывают исключения, к примеру когда неправильно определен z-index, подробнее можно прочитать здесь https://blog.logrocket.com/eliminate-content-repaints-with-the-new-layers-panel-in-chrome-e2c306d4d752/.
Не забывайте также что нельзя злоупотреблять этапом composite и создавать слой на каждый элемент, так как для хранения каждого слоя используется память. Злоупотребляя свойствами will-change или transform, вы можете значительно увеличить память потребляемую вашей страницей в результате чего ваша страница может просто перестать работать.
Для работы со слоями в Chrome есть отличный инструмент Layers.
В этой статье я попытался вам рассказать о процессах Reflow, Repaint, Composite. Изучив как они работают, можно сделать вывод о том, как важно сохранять более плоскую структуру вашего html документа, минимизировать изменения элементов и обращения через JS, а так же почему следует использовать свойства transform, opacity, will-change для более плавной анимации.