Chrome Dev Summit 2018 is happening now and streaming live on YouTube. Watch now.

Ограничение объема и сложности вычисления стилей

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

TL;DR

  • Снижайте сложность своих селекторов; используйте методологию, основанную на классах, например BEM.
  • Сокращайте количество элементов, для которых требуется выполнять вычисление стилей.

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

Вторая часть процесса состоит в получении всех правил стилей из соответствующих селекторов и определении стилей, которые в итоге имеет элемент. В Blink (движке визуализации Chrome и Opera) эти процессы на сегодня более или менее одинаковы по затратам:

Приблизительно 50 % времени, которое тратится на вычисление стиля элемента, уходит на сопоставление селекторов, а вторую половину времени занимает построение RenderStyle (представления стиля) на основе сопоставленных правил.

Руне Лиллесвеен (Rune Lillesveen), Opera / Аннулирование стилей в Blink

Снижайте сложность своих селекторов

В самом простом случае вы ссылаетесь на элемент в своем CSS просто с помощью класса:

.title {
  /* styles */
}

Однако, по мере роста проекта, CSS, скорее всего, станет сложнее, и в конечном итоге селекторы могут выглядеть примерно следующим образом:

.box:nth-last-child(-n+1) .title {
  /* styles */
}

Для того чтобы знать, что стили нужно применить, бразер должен фактически спросить: "Это тот элемент с классом title, у которого есть родительский элемент, который является минус n плюс 1 дочерним элементом элемента с классом box?" В зависимости от используемого селектора и браузера на вычисление всего этого может уйти масса времени. Вместо этого, необходимое поведение селектора можно преобразовать в класс:

.final-box-title {
  /* styles */
}

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

Сокращайте количество элементов, которым заданы стили

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

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

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

Оценивайте затраты на перерасчет стилей

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

Программа DevTools, информирующая о длительном вычислении стилей.

Полоса вверху обозначает количество кадров в секунду. Если вы видите, что столбцы уходят выше нижней линии (60 кадров в секунду), значит, у вас есть длительные кадры.

Проблемная область, выделенная в Chrome DevTools.

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

Если же есть большой фиолетовый блок (как на приведенном выше примере), щелкните запись, и программа выдаст более подробные сведения.

Получение подробных сведений о длительных вычислениях стилей.

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

Если щелкнуть само событие, программа выдаст стек вызовов, в котором будет выделено место в коде JavaScript, которое отвечает за инициирование изменения стиля. Помимо этого, вы получите количество элементов, которые были затронуты изменением (в данном случае их чуть больше 400), а также время, которое потребовалось на выполнение вычисления стилей. Эта информация позволяет начать поиск соответствующих мест в своем коде и их исправление.

Используйте блок, элемент, модификатор

Такие подходы к программированию, как BEM (Block, Element, Modifier – блок, элемент, модификатор), фактически основаны на описанной выше работе по сопоставлению селекторов, поскольку в них рекомендуется, чтобы у всех элементов был один класс, а когда требуется иерархия, она также должна основываться на имени этого класса:

.list { }
.list__list-item { }

Если вам необходим определенный модификатор, как в приведенном выше примере, когда нам требовалось сделать нечто особенное с последним потомком, его можно добавить вот так:

.list__list-item--last-child {}

Если вы ищете хороший способ организации своего кода CSS, BEM – это действительно неплохая отправная точка, как с точки зрения структуры, так в силу упрощения определения стилей.

Если BEM вам не нравится, есть другие варианты организации CSS. Однако, помимо эргономики подхода, следует оценивать и вопросы производительности.

Ресурсы