[Высокопроизводительный JS] Методы перерисовки, перекомпоновки и оптимизации браузера

браузер

Ссылка на эту статью: "Высокопроизводительный JS",Подробное объяснение управления производительностью веб-страницы

Базовые знания

После того, как браузер загрузил все компоненты страницы — HTML-теги, JS, CSS, изображения — он анализирует и генерирует две внутренние структуры данных:

  • Дерево DOM: представляет структуру страницы
  • Дерево рендеринга: представляет, как отображаются узлы DOM.

процесс создания веб-страницы

  1. HTML анализируется в дерево DOM парсером HTML
  2. CSS анализируется в CSSOM (объектная модель CSS) синтаксическим анализатором CSS.
  3. вложение дерева DOM и CSSOM, создание дерева рендеринга
  4. Генерировать компоновку (поток), то есть плоскую композицию всех узлов всех деревьев рендеринга
  5. нарисовать макет на экране

Два этапа «создание макета» (поток) и «рисование» (рисование) вместе называются «оказывать"(оказывать).

Когда веб-страница создается, она будет отображаться как минимум один раз. В процессе доступа пользователя он будет продолжать перерисовываться.

Определение узла

Каждый узел дерева DOM должен быть присутствует, присутствует как минимум на одном соответствующем узле рендеринга дерева (без скрытого элемента, соответствующего рендеринга дерева узла DOM).

Узлы в дереве рендеринга называются «фреймами» или «боксами» в соответствии с определением модели CSS.

Перекомпоновать и перекрасить

определение

  • Что такое перестановка:Регенерировать макет. Когда изменения DOM влияют на геометрию элемента (ширину и высоту) — например, изменение ширины границы или добавление текста в абзац для увеличения количества строк — браузеру необходимо пересчитать геометрию элемента, а также геометрию и положение других элементов. элементы также будут затронуты. Браузер аннулирует затронутую часть дерева рендеринга и перестраивает дерево рендеринга. Этот процесс называется перегруппировкой.

  • Что такое перекраска:перекрашивать. После завершения перекомпоновки браузер перерисовывает затронутую часть на экране. Этот процесс называется перекрашиванием.

Взаимосвязь между перестановкой и перекрашиванием

Перекомпоновка должна привести к перерисовке, перерисовка не обязательно приводит к перекомпоновке. Если изменения DOM не влияют на свойства геометрии, а макет элемента не изменился, происходит только одна перерисовка (перекомпоновка не требуется).

дело о перестановке

Возникновение «перестановки» при изменении макета страницы и геометрических свойств. следующим образом:

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

Перестановка происходит диапазон

Целая страница или часть. Например, перестановка всей страницы запускается при появлении полосы прокрутки.

влияние на производительность

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

Чтобы улучшить производительность веб-страницы, нужно уменьшить частоту и стоимость «переформатирования» и «перерисовки» и как можно меньше запускать повторную визуализацию.

Очередь изменений дерева рендеринга

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

div.style.color = 'blue';
div.style.marginTop = '30px';

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

Если он плохо написан, это вызовет два перекомпоновки и перерисовки.

div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

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

Принудительно очистить очередь

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

  • offsetTop, offsetLeft, offsetWidth, offsetHeight
  • scrollTop, scrollLeft, scrollWidth, scrollHeight
  • clientTop, clientLeft, clientWidth, clientHeight
  • getComputedStyle() (currentStyle in IE)

clientTop: Толщина границы элемента. Если толщина границы не указана, она обычно равна 0.

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

offsetTop: получает высоту объекта сверху относительно родительской координаты (элемент с позиционированием css или элемент тела), заданный свойством offsetParent.

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

scrollHeight: IE и Opera считают, что scrollHeight — это фактическая высота содержимого веб-страницы, которая может быть меньше, чем clientHeight. FF считает, что scrollHeight — это высота содержимого веб-страницы, но минимальное значение — это clientHeight.

offsetHeight: получает высоту объекта относительно родительской координаты (позиционированного элемента css или элемента тела), указанной свойством offsetParent. IE и Opera считают, что offsetHeight = clientHeight + полоса прокрутки + граница. FF считает, что offsetHeight — это фактическая высота содержимого веб-страницы, которая может быть меньше, чем clientHeight. В новой версии FF и IE значение offsetHeight одинаковое, оно указывает высоту веб-страницы, которая не имеет ничего общего с полосой прокрутки, Chrome не включает полосу прокрутки.

Метод Window.getComputedStyle() возвращает объект, который сообщает значения всех свойств CSS элемента после применения активной таблицы стилей и анализа любых базовых вычислений, которые могут содержаться в этих значениях. Доступ к частным значениям свойства CSS можно получить через API, предоставляемый объектом, или просто путем индексации с использованием имени свойства CSS.

Решение

Следовательно, с точки зрения производительности, постарайтесь не писать операцию по работе и записи, поставить его в оператор.

// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";

// good
var left = div.offsetLeft;
var top  = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";

Общие правила таковы:

  • Чем проще таблица стилей, тем быстрее она будет перекомпоновываться и перерисовываться.
  • Чем выше уровень элементов DOM для перекомпоновки и перерисовки, тем выше стоимость.
  • Стоимость перестановки и перерисовки элементов таблицы выше, чем у элементов div.

Методы оптимизации браузера

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

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

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

// 优化前
myElement.style.left = 1 + myElement.offsetLeft + 'px';
myElement.style.top = 1 + myElement.offsetTop + 'px';
if (myElement.offsetLeft >= 500) {
    stopAnimation();
}

// 优化后
// 获取一次起始位置的值,然后赋值给一个变量,在动画循环中直接使用变量不再查询偏移量
var current = myElement.offsetLeft;
current++;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if (myElement.offsetLeft >= 500) {
    stopAnimation();
}

2. Объедините несколько изменений в DOM и стилях: используйтеcssTextАтрибуты

Большинство браузеров теперь автоматически оптимизируются

// 优化前
 var el = document.getElementById('mydiv');
 el.style.borderLeft = '1px';
 el.style.borderRight = '2px';
 el.style.padding = '5px';
 
 // 优化后
 var el = document.getElementById('mydiv');
 el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';

3. При изменении объединенного стиля: измените cssclassимя вместо изменения встроенных стилей

 var el = document.getElementById('mydiv');
 el.className = "active";

4. Уберите элемент из потока документов, измените его и верните элемент обратно в документ.

var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul, data); // 更新指定节点数据的函数
ul.style.display = 'block';

5. (Рекомендуется) Создайте и обновите фрагмент документа за пределами документа, а затем добавьте его в исходный список.

Фрагмент документа — это легкий объект документа, используемый для обновления и перемещения узлов. Когда вы прикрепляете фрагмент к узлу, вы фактически добавляете дочерние элементы фрагмента, а не сам фрагмент.

Этот метод производит наименьшее количество обхода DOM и перекомпоновки.

//创建一个文档片段
var fragment = document.createDocumentFragment();

// 更新文档片段的数据
appendDataToElement(fragment, data);

// 将文档片段附加到原始列表中(实际添加的是子节点)
document.getElementById('mylist').appendChild(fragment);

Примеры следующие:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>使用fragment进行重排重绘</title>
</head>
<body>
<ul id="myList">
  <li>a</li>
  <li>b</li>
</ul>
<p>
  向上面的ul中加入两个新的li,比较使用fragment和不使用的性能
</p>

<script>
  console.time(0);
  var newLi1 = document.createElement('li');
  newLi1.innerHTML = 'c';

  var newLi2 = document.createElement('li');
  newLi2.innerHTML = 'd';

  document.getElementById('myList').appendChild(newLi1);
  document.getElementById('myList').appendChild(newLi2);
  console.timeEnd(0)

  console.time(1);
  var fragment = document.createDocumentFragment();
  var newLi1 = document.createElement('li');
  newLi1.innerHTML = 'c';

  var newLi2 = document.createElement('li');
  newLi2.innerHTML = 'd';

  fragment.appendChild(newLi1);
  fragment.appendChild(newLi2);

  document.getElementById('myList').appendChild(fragment);
  console.timeEnd(1)
</script>
</body>
</html>

6. Сделайте резервную копию узла, работайте с копией и замените старый узел узлом копии после завершения.

var old = document.getElementById('mylist');

// 对旧节点备份
var clone = old.cloneNode(true);

appendDataToElement(clone, data);

// 用副本节点代替旧节点
old.parentNode.replaceChild(clone, old);

7. Уберите элементы из потока анимации

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

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

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

  1. использоватьАбсолютно позиционировать анимированный элемент на странице, убрать из документооборота
  2. Заставьте элемент двигаться. Когда он расширится, онВременно закрыть часть страницы. Но это только процесс перерисовки небольшого участка страницы, и большая часть содержимого страницы не будет переформатирована и перерисована.
  3. когдаВосстановление позиционирования после окончания анимации, тем самым перемещая другие элементы документа вниз только один раз.