CSS-live.ru

10 способов уменьшить количество перекомпоновок и повысить производительность

Перевод статьи 10 Ways to Minimize Reflows and Improve Performance с сайта www.sitepoint.com, автор — Крейг Баклер.

Хотя веб-страницы достигают 2МБ, производительность остается острой темой. Чем шустрее ваше приложение, тем лучше пользовательский опыт и выше конверсия.

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

Перерисовка

Перерисовка происходит, когда изменения элементов влияют на видимость, а не на раскладку. Например, opacity, background-color, visibility и outline. Перерисовка — ресурсозатратный процесс, поскольку браузер вынужден проверять видимость всех других нод в DOM, из которых одна или несколько могли стать видимыми под изменившимся элементом.

Перекомпоновка

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

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

Полезно понимать, когда запускается перекомпоновка:

Добавление, удаление или изменение видимых элементов DOM

Первое очевидно: применение JavaScript для изменения DOM приведёт к перекомпоновке.

Добавление, удаление или изменение CSS-стилей

Так же непосредственное применение CSS-стилей или изменение класса может изменить макет. Изменение ширины элемента может повлиять на все элементы в той же и соседних ветках.

CSS3-анимации и переходы

Каждый кадр анимации приводит к перекомпоновке

Применение offsetWidth и offsetHeight

Как ни странно, чтение свойств элемента offsetWidth и offsetHeight может сначала вызвать перекомпоновку, чтобы можно было рассчитать их значения.

Действия пользователя

Наконец, пользователь может вызвать перекомпоновки при :hover-эффекте, вводе текста в поле, изменении размера окна, переключении таблиц стилей или шрифтов.

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

1. Используйте передовые методы для раскладки

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

Встроенные стили повлияют на раскладку во время загрузки HTML и вызовут дополнительную перекомпоновку. Таблицы ресурсозатратные, поскольку парсеру требуется несколько проходов для вычисления размеров ячейки.  Применение table-layout: fixed поможет при представлении табличных данных, поскольку ширина колонки зависит от содержимого ячеек шапки таблицы.

Применение flexbox для основного макета страницы также может оказать воздействие на производительность, поскольку положение и размеры flex-элементов могут меняться во время загрузки HTML.

2. Уменьшайте количество CSS-правил

Чем меньше правил вы используете, тем быстрее перекомпоновка. По возможности следует также избегать сложных CSS-селекторов.

Это может стать особенно серьезной проблемой, если вы используете фреймворки типа Bootstrap —  большинство сайтов использует лишь малую часть его стилей. Инструменты типа Unused CSS, uCSS, grunt-uncss и gulp-uncss могут значительно сократить определения стилей и размеры файла.

3. Уменьшите глубину DOM-дерева

Ещё сложнее — уменьшить размер DOM-дерева и число элементов в каждой ветке. Чем меньше размер и глубина вложенности документа, тем быстрее он может быть перекомпонован. Если вы не поддерживаете старые браузеры, то вполне возможно удалить ненужные обёрточные элементы.

4. Обновляйте классы как можно ниже по DOM-дереву

Меняйте классы элементам как можно ниже в DOM-дереве (т.е. элементам без множественных глубоко вложенных дочерних элементов). Этим можно ограничить область видимости перекомпоновки минимально достаточным количеством нод. По сути, меняйте классы родительским нодам типа оберток лишь при условии, что это минимально затронет вложенные ноды.

5. Удалите сложные анимации из потока

Убедитесь, что анимации применяются к одному элементу, удалив его из потока документа при помощи position: absolute; или position: fixed;. Это позволяет менять размеры и позиции, не затрагивая другие элементы в документе.

6. Модифицируйте скрытые элементы

Элементы, скрытые с попощью display: none;, не вызовут перерисовку и перекомпоновку при изменении.  На практике, произведите изменения элемента прежде чем делать его видимыми.

7. Обновляйте элементы «пачками»

Производительность можно улучшить, обновив все DOM-элементы в одной операции. Этот простой пример приводит к трём перекомпоновкам.

var myelement = document.getElementById('myelement');
myelement.width = '100px';
myelement.height = '200px';
myelement.style.margin = '10px';

Можно уменьшить это до одной перекомпоновки, которую также легче поддерживать, например.

var myelement = document.getElementById('myelement');
myelement.classList.add('newstyles');
.newstyles {
  width: 100px;
  height: 200px;
  margin: 10px;
}

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

  • item 1
  • item 2
  • item 3

Добавление каждого элемента по очереди вызывает до семи перекомпоновок — одну при добавлении <ul>, три для каждой <li> и три для текста. Но можно обойтись единственной перекомпоновкой, например, воспользовавшись DOM-фрагментом и сначала выстраивая ноды в памяти.

var
  i, li,
  frag = document.createDocumentFragment(),
  ul = frag.appendChild(document.createElement('ul'));

  for (i = 1; i <= 3; i++) {
    li = ul.appendChild(document.createElement('li'));
    li.textContent = 'item ' + i;
  }

document.body.appendChild(frag);

8. Ограничивайте количество затрагиваемых элементов

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

9. Плавность — угроза для производительности

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

10. Анализируйте проблемы с перерисовкой в браузерных инструментах

Все основные браузеры предоставляют инструменты разработчика, которые подсвечивают, как перекомпоновка влияет на производительность. В браузерах Blink/Webkit, таких как Chrome, Safari и Opera, откройте панель «Шкала времени» и запишите активность:

1435764024006-minimize-reflow-1

Похожая панель «Шкала времени» есть в инструментах разработчика Firefox:

1435764030006-minimize-reflow-2

В инструментах разработчика Internet Explorer (F12) такая панель называется «Отклик пользовательского интерфейса»:

1435764035006-minimize-reflow-3

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

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

P.S. Это тоже может быть интересно:

4 комментария

  1. также можно добавить transleteZ(0) — винесен на новий слой и не будет перерисовивать все дом-дерево

    1. Если при этом будут меняться width/height и т.п. — дерево всё равно будет перестраиваться и перерисовываться. Шаманства с композитными слоями (это, более стандартный вариант с will-change и т.п.) хороши как дополнение, когда остальные источники нагрузки (влияние на соседей и т.п.) устранены. И то в меру, т.к. побочные эффекты от создания лишних композитных слоев тоже случаются.

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

Добавить комментарий для lobo Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

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