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

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

Перерисовка

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

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

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

И то, и другое блокирует браузер — ни пользователь, ни приложение не могут выполнять другие задачи при перерисовке или перекомпоновке. Это одна из причин таких проблем как дискретная прокрутка или неотзывчивые интерфейсы.

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

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

Это очевидно — использование JavaScript для изменения DOM влечет перекомпоновку.

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

Схожим образом, применение стилей CSS или изменение класса, может повлиять на все элементы ветки DOM и ее соседей.

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

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

Использование offsetWidth и offsetHeight

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

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

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

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

1.Используйте лучшие практики для раскладки

Не могу поверить, что я вынужден повторять в 2015-м году: не используйте инлайновые стили или таблицы для раскладки.

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

Использование flexbox для раскладки страницы также может повлечь проблемы с производительностью, так как позиции и размеры отдельных флекс-элементов могут измениться после загрузки 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

Добавление каждого элемента по отдельности повлечет 7 перекомпоновок — одну для <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. Идите на компромисс в плавности ради производительности.

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

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

Все основные браузеры предоставляют средства разработки, позволяющие выявлять, как перекомпоновка влияет на производительность. В браузерах на движках Blink/Webkit (Chrome, Safari и Opera) есть панель Timeline:

minimize-reflow

И аналогичная панель в Firefox:

minimize-reflow-2

В IE аналог называется UI Responsiveness:

minimize-reflow-3

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