Новое CSS-свойство content-visibility ускоряет отрисовку страницы в несколько раз
5 августа 2020 разработчики Google анонсировали новое CSS-свойство content-visibility
в версии Chromium 85. Оно должно существенно повлиять на скорость первой загрузки и первой отрисовки на сайте; причём с только что отрендеренным контентом можно взаимодействовать сразу же, не дожидаясь загрузки остального содержимого. content-visibility
заставляет юзер-агент пропускать разметку и покраску элементов, не находящихся на экране. По сути, это работает как lazy-load, только не на загрузке ресурсов, а на их отрисовке.
В этой демке content-visibility: auto
, применённый к разбитому на части контенту, даёт прирост скорости рендера в 7 раз
Поддержка
content-visibility
основывается на примитивах из спецификации CSS Containment. Хотя на данный момент content-visibility
поддерживается только в Chromium 85 (и считается «достойным прототипирования» в Firefox), спецификация Containment поддерживается в большинстве современных браузеров.
Принцип работы
Основная цель CSS Containment — повысить производительность рендеринга веб-контента, обеспечивая предсказуемую изоляцию поддерева DOM от остальной части страницы.
По сути, разработчик может сообщить браузеру, какие части страницы инкапсулированы как набор контента, что позволяет браузерам оперировать контентом без необходимости учитывать состояние вне поддерева. Браузер может оптимизировать рендеринг страницы, зная, какие поддеревья содержат изолированный контент.
Всего есть четыре типа CSS Containment, каждое выступает значением для CSS-свойства contain
и может быть скомбинировано с другими:
size
: Ограничение размера элемента гарантирует, что блок элемента может быть размещен без необходимости изучения его потомков. То есть, зная размер элемента, мы вполне можем опустить вычисление расположения его потомков.layout
: Ограничение выкладки не даёт потомкам повлиять на внешнее расположение других блоков на странице. Это позволяет нам потенциально опустить размещение потомков, если все, что мы хотим сделать, это расположить другие блоки.style
: Ограничение стилей гарантирует, что свойства, влияющие не только на его потомков, не покидают элемент (например, счетчики). Это позволяет пропустить вычисление стилей для потомков, если все, что нам нужно, — это вычислить стили для других элементов.paint
: Ограничение покраски не позволяет потомкам отображаться за пределами своего контейнера. Ничего не будет налезать на элемент, и если он находится за границами экрана или так или иначе невидим, его потомки также будут невидимы. Это позволяет не отрисовывать потомков, если элемент уже за краями экрана.
Пропускаем рендеринг с content-visibility
Может быть непонятно, какие значения contain
лучше использовать, поскольку оптимизация браузера может сработать только при правильно указанном наборе параметров. Стоит поиграться со значениями, чтобы эмпирическим путём узнать, что работает лучше всего. А лучше использовать content-visibility
для автоматической настройки contain
. content-visibility: auto
гарантирует максимальный возможный прирост производительности при минимальных усилиях.
В автоматическом режиме свойство получает атрибуты layout, style и paint, а при выходе элемента за края экрана, получает size и перестаёт красить и проверять содержимое. Это значит, что как только элемент выходит из зоны отрисовки, его потомки перестают рендериться. Браузер распознает размеры элемента, но больше не делает ничего, пока в отрисовке не возникнет необходимость.
Пример — тревел-блог
Your browser does not support HTML5 video.
Обычно в тревел-блоге есть несколько историй с изображениями и описания. Вот что происходит в типичном браузере, когда он переходит на тревел-блог:
- Часть страницы загружается из сети вместе с необходимыми ресурсами
- Браузер стилизует и размещает весь контент на странице, не различая контент на видимый и невидимый
- Браузер переходит к шагу 1 до того пока не загрузит все ресурсы
На шаге 2 браузер обрабатывает содержимое, пытаясь найти изменения. Он обновляет стили и расположение любого нового элемента вместе с элементами которые могли быть изменены в результате обновлений. Это рендеринг. Он занимает время.
Теперь представим, что мы поместили content-visibility: auto
на каждый пост в блоге. Основная система та же: браузер загружает и рендерит части страницы. Однако, разница в количестве работы, сделанной в шаге 2. С content-visibility
браузер будет стилизовать и размещать тот контент который сейчас видит пользователь (на экране). Но обрабатывая истории вне экрана, браузер будет пропускать рендеринг всего элемента и будет размещать только контейнер. Производительность загрузки этой страницы будет как если бы она содержала заполненные посты на экране и пустые контейнеры для каждого поста вне его. Так получается гораздо быстрее, выигрыш составляет до 50% от времени загрузки. В нашем примере мы видим улучшение с 232 мс рендеринга до 30 мс, это улучшение производительности в 7 раз.
Что нужно сделать чтобы воспользоваться этими преимуществами? Во-первых, мы разделяем контент на части:
После, мы применяем последующую стилизацию на части:
.story {
content-visibility: auto;
contain-intrinsic-size: 1000px; /* Объяснено далее */
}
Обратите внимание, что когда контент перемещается с экрана, он будет рендериться по необходимости. Однако, это не означает что браузер должен будет рендерить один и тот же контент раз за разом, ведь работа рендеринга сохраняется, когда это возможно.
Определение типичного размера элемента с contain-intrinsic-size
Для того чтобы понять потенциальные преимущества content-visibility
, браузер должен применять ограничения по размеру, чтобы гарантировать, что результаты рендеринга контента не будут влиять на размеры элементов. Это означает что элемент будет размещен как если бы он был пустой. Если у элемента не определена высота, то она будет равна нулю.
К счастью, у css есть ещё одна способность, contain-intrinsic-sizе
, которая предоставляет возможность определить настоящий размер элемента, если тот подвергся сжатию. В нашем примере мы устанавливаем ширину и высоту примерно 1000 px.
Это означает, что он будет располагаться так, будто там один файл «внутренного размера», при этом гарантируя, что div все ещё занимает место. contain-intrinsic-sizе выступает в качестве заполнителя.
Скрываем контент с content-visibility: hidden
content-visibility: hidden
делает то же, что и content-visibility: auto
делает с контентом вне экрана. Однако, в отличие от auto, он не начинает автоматический рендеринг контента на экране.
Сравним его с обычными способами спрятать контент элемента:
- display: none: скрывает элемент и удаляет состояние рендеринга. Это означает что доставая элемент будет стоить той же нагрузки как и создание нового элемента.
- visibility: hidden: скрывает элемент и оставляет состояние рендеринга. Это на самом деле не удаляет элемент из документа, так как он (и его поддерево) все еще занимает геометрическое пространство на странице и по-прежнему может быть нажат. Он также обновляет состояние рендеринга в любое время, когда это необходимо, даже если он скрыт.
content-visibility: hidden
, с другой стороны, скрывает элемент, сохраняя состояние рендеринга, так что если будут необходимы какие-либо изменения, они произойдут только при показе элемента на экране.
Заключение
content-visibility
и CSS Containment Spec позволяют значительно ускорять рендеринг и загрузку страниц без каких-либо сложных манипуляций, на голом CSS.
The CSS Containment Spec
MDN Docs on CSS Containment
CSSWG Drafts
На правах рекламы
Серверы для размещения сайтов — это про наши эпичные! Все серверы «из коробки» защищены от DDoS-атак, автоматическая установка удобной панели управления VestaCP. Лучше один раз попробовать ;)