1. Перерисовать и переставить
1.1 Что такое перерисовка и перекомпоновка
Под перерисовкой понимается модификация некоторых стилей, при этом положение и размер элементов не изменяются; Перекомпоновка означает, что положение или размер элемента изменились, браузеру необходимо пересчитать дерево рендеринга, и после того, как новое дерево рендеринга установлено, браузер перерисует затронутый элемент.
1.2 Страница рендеринга в браузере
Когда вы идете на собеседование, вам всегда будут задавать вопрос, то есть «Что происходит, когда вы вводите строку url в браузере?» Ответ на этот вопрос должен отвечать не только знанию сети, но и также включает в себя отображение страницы браузером. Когда наш браузер получает ответ от сервера, он начинает рендеринг построчно. Когда он сталкивается с CSS, он асинхронно вычисляет значение атрибута, а затем продолжает синтаксический анализ DOM. После завершения синтаксического анализа дерево DOM будет формируется, который будет асинхронно вычисляться. Хороший стиль (блок стиля) объединяется с деревом DOM, чтобы стать деревом рендеринга, которое затем рисуется на странице браузером. Разница между деревом DOM и деревом рендеринга заключается в том, что узлы со стилем display:none; будут в дереве DOM, но не в дереве рендеринга. После того, как браузер отрисовывает, он начинает анализировать файл js и определяет, нужно ли перерисовывать и переупорядочивать в соответствии с js.
1.3 Причины перерисовки и перестановки
Факторы, вызывающие перекрашивание:
- Изменение атрибутов стиля, таких как видимость, контур, цвет фона и т. д., не меняет размер, положение и т. д. элемента. Браузер перерисует на основе новых свойств элемента.
Факторы, вызывающие перестройку:
- Изменения содержания
- Изменения текста или изменения размера изображения
- Изменения геометрических свойств элементов DOM
- Например, когда ширина и высота элемента DOM изменены, соответствующие узлы в исходном дереве рендеринга станут недействительными, и браузер переупорядочит соответствующие узлы в дереве рендеринга в соответствии с измененным DOM. Если геометрические свойства родительского узла изменяются, это также заставит его дочерние узлы и последующие одноуровневые узлы пересчитать положение и т. д., что приведет к серии перестановок.
- Изменения структуры дерева DOM
- Добавление узлов DOM, изменение положения узлов DOM и удаление узла — все это изменения в дереве DOM, которые вызывают переупорядочение страниц. Макет браузера — это процесс сверху вниз, изменение текущего элемента не повлияет на элементы, которые были пройдены перед ним, но если новый элемент будет добавлен перед всеми узлами, все последующие элементы будут переставлены.
- получить некоторые свойства
- Помимо прямого изменения дерева рендеринга, браузер также перестраивается для получения правильного значения при получении значений некоторых свойств, к которым относятся: offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop, scrollLeft, scrollWidth, scrollHeight, clientTop , clientLeft , clientWidth, clientHeight, getComputedStyle().
- Изменение размера окна браузера
- Изменение размера окна повлияет на изменение размера элементов во всей веб-странице, то есть на изменение установленного атрибута DOM-элемента, поэтому вызовет перестановку.
- Внешний вид полос прокрутки (которые вызывают перекомпоновку всей страницы)
Короче говоря, вам нужно знать, что js является однопоточным.Перерисовка и переупорядочивание блокируют пользовательские операции и влияют на производительность веб-страниц.Когда страница перерисовывается и переупорядочивается несколько раз, например, напишите таймер для изменения элементов страницы каждый 500ms ширина и высота, то эта страница может все больше и больше застревать, мы должны максимально уменьшить перерисовку и перекомпоновку. Тогда наша оптимизация DOM также основана на этом начале.
2. Оптимизация
2.1 Ограничение доступа
Сокращение количества посещений естественно означает кеширование элементов, но обратите внимание
var ele = document.getElementById('ele');
Это не кеширование ele.Каждый вызов ele эквивалентен посещению узла с идентификатором ele.
2.1.1 Список узлов кэша
var foods = document.getElementsByClassName('food');
Мы можем использовать food[i] для доступа к i-му элементу с классом food, но food здесь не массив, а NodeList. NodeList похож на массив, который содержит несколько упорядоченных узлов и может получить доступ к этим узлам по положению. Объект NodeList является динамическим, и для каждого посещения выполняется запрос на основе документа. Итак, мы хотим свести к минимуму количество посещений NodeList, мы можем рассмотреть возможность кэширования значения NodeList.
// 优化前
var lis = document.getElementsByTagName('li');
for(var i = 0; i < lis.length; i++) {
// do something...
}
// 优化后,将length的值缓存起来就不会每次都去查询length的值
var lis = document.getElementsByTagName('li');
for(var i = 0, len = lis.length; i < len; i++) {
// do something...
}
И поскольку odelist динамичен, если он не кэшируется, он может вызвать бесконечную петлю, такую как добавление элементов, получая длину odelist.
2.1.2 Смена селектора
Есть два наиболее распространенных способа получения элементов — getElementsByXXX() и queryselectorAll(), разница между этими двумя селекторами очень большая: первый — для получения динамических коллекций, а второй — для получения, например, статических коллекций.
// 假设一开始有2个li
var lis = document.getElementsByTagName('li'); // 动态集合
var ul = document.getElementsByTagName('ul')[0];
for(var i = 0; i < 3; i++) {
console.log(lis.length);
var newLi = document.createElement('li');
ul.appendChild(newLi);
}
// 输出结果:2, 3, 4
// 优化后
var lis = document.querySelectorAll('li'); // 静态集合
var ul = document.getElementsByTagName('ul')[0];
for(var i = 0; i < 3; i++) {
console.log(lis.length);
var newLi = document.createElement('li');
ul.appendChild(newLi);
}
// 输出结果:2, 2, 2
Операции со статическими коллекциями не вызывают повторных запросов документов и более оптимизированы, чем динамические коллекции.
2.1.3 Избегайте ненужных циклов
// 优化前
for(var i = 0; i < 10; i++) {
document.getElementById('ele').innerHTML += 'a';
}
// 优化后
var str = '';
for(var i = 0; i < 10; i++) {
str += 'a';
}
document.getElementById('ele').innerHTML = str;
Код до оптимизации обращается к элементу ele 10 раз, тогда как код после оптимизации обращается к нему только один раз, что значительно повышает эффективность.
2.1.4 Делегирование мероприятия
Функции мероприятия в JS являются все объекты. Если есть слишком много функций событий, он займет много памяти, и тем больше DOM элементов, связанные с событием, тем более раз доступа к доме, и интерактивное время доступа к Страница также будет отложена. Следовательно, родилась делегация событий. Делегация событий использует пузырька событий, и можно указать только один обработчик событий для управления всеми событиями определенного типа.
// 事件委托前
var lis = document.getElementsByTagName('li');
for(var i = 0; i < lis.length; i++) {
lis[i].onclick = function() {
console.log(this.innerHTML);
};
}
// 事件委托后
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(event) {
console.log(event.target.innerHTML);
};
Мы посетили перед делегатом события lis.length раз li, но после использования делегатов события мы посетили только один раз ul.
2.2 Уменьшить перерисовку и перестановку
2.2.1 Изменение нескольких стилей узла dom
Мы хотим изменить ширину и высоту элемента div, обычно это может быть так
var div = document.getElementById('div1');
div.style.width = '220px';
div.style.height = '300px';
Описанная выше операция изменяет два атрибута элемента, трижды посещает DOM, запускает два перекомпоновки и две перерисовки. Мы сказали, что оптимизация заключается в уменьшении количества посещений и уменьшении количества перерисовок и повторных заказов.От этой отправной точки можем ли мы посетить элемент только один раз и уменьшить количество повторных заказов до 1? Очевидно да, мы можем написать класс в css
/* css
.change {
width: 220px;
height: 300px;
}
*/
document.getElementById('div').className = 'change';
Таким образом, вы можете работать с несколькими стилями одновременно.
2.2.2 Пакетное изменение стиля узла dom
Случай с приведенным выше кодом относится к узлу dom. Что, если мы хотим изменить стиль коллекции dom? Первый метод, который приходит на ум, — пройтись по коллекции и добавить className к каждому узлу. Подумайте еще раз, не посещает ли он узел dom несколько раз? Подумайте о разнице между деревом dom и деревом рендеринга, упомянутым в начале статьи.Если атрибут отображения узла равен none, то этот узел не будет существовать в дереве рендеринга, а значит, работа этого узла будет не повлияет на дерево рендеринга и, следовательно, не вызовет перерисовку и перестановку, исходя из этой идеи, мы можем добиться оптимизации:
- Показать родительский элемент коллекции, который нужно изменить display: none;
- Затем перейдите и измените узлы коллекции
- Установит родительский элемент display: block;
// 假设增加的class为.change
var lis = document.getElementsByTagName('li');
var ul = document.getElementsByTagName('ul')[0];
ul.style.display = 'none';
for(var i = 0; i < lis.length; i++) {
lis[i].className = 'change';
}
ul.style.display = 'block';
2.2.3 DocumentFragment
Сторона createDocumentFragment() используется для создания объекта виртуального узла, или, другими словами, используется для создания узла фрагмента документа. Он может содержать различные типы узлов и изначально пуст.
Узлы DocumentFragment не принадлежат дереву документа, а унаследованное свойство parentNode всегда имеет значение null. Он имеет очень полезную функцию при запросе вставки узла DocumentFragment в дерево документа,Вставляется не сам DocumentFragment, а все его дочерние узлы.Эта функция делает DocumentFragment заполнителем, временно сохраняя узлы, которые вставляются в документ одновременно.
Кроме того, когда необходимо добавить несколько элементов DOM, если эти элементы сначала добавляются в DocumentFragment, а затем DocumentFragment равномерно добавляется на страницу, количество раз, которое страница отображает DOM, будет уменьшено, а эффективность будет снижена. быть значительно улучшена.
Инструкции:
var frag = document.createDocumentFragment(); //创建一个DOM片段
for(var i=0;i<10000;i++) {
var li = document.createElement("li");
li.innerHTML = i;
frag.appendChild(li); //将li元素加到文档碎片上
}
ul.appendChild(frag); //将文档碎片加到ul上
3. Резюме
- Сократить количество посещений дома
- Значения атрибутов узла кэширования
- Использование селекторов
- Избегайте ненужных циклов
- делегация мероприятия
- Уменьшите количество перерисовок и перекомпоновок
- Используйте className для изменения нескольких стилей
- Исключить родительский элемент из потока документов и возобновить его
- DocumentFragment
Если я увижу другие решения по оптимизации в будущем, я обновлю их, и вы можете связаться со мной.
Справочная документация: