优化javascript代码来提高性能之减少重绘和重排

101 阅读2分钟

概念介绍

重排

重排是指浏览器重新计算元素的几何属性(位置、大小等)的过程。当 DOM 发生变化时,如元素的尺寸、位置或布局属性发生变化,浏览器需要重新计算这些属性以更新页面的布局。重排是性能开销较大的操作,因为它可能需要对页面的大部分或全部元素进行计算。

重绘

重绘是指在元素的几何属性变化后,浏览器重新绘制元素外观的过程。重绘通常比重排的开销小,因为它只影响元素的外观,不需要重新计算布局。

减少重绘和重排的原因

  1. 性能开销:重排和重绘是性能密集型的操作,它们会导致浏览器花费额外的时间来处理这些任务,从而影响页面的响应速度和动画的流畅度。
  2. 流畅度:在动画和滚动等场景中,频繁的重绘和重排会导致帧率下降,使得动画看起来卡顿,影响用户体验。
  3. 资源浪费:不必要的重绘和重排会浪费 CPU 和 GPU 资源,尤其是在移动设备上,这些资源更加有限。
  4. 内存使用:频繁的重排和重绘可能会导致内存使用增加,尤其是在处理大量 DOM 元素时。
  5. 响应时间:减少重绘和重排可以减少用户操作的响应时间,使得页面更加灵敏。

如何减少重绘和重排

1. 使用 transform 和 opacity 属性

这些属性的变化不会引起重排,因为它们可以在合成层上进行。

css
/* CSS */
.item {
  transition: transform 0.3s, opacity 0.3s;
}

/* JavaScript */
const item = document.querySelector('.item');
item.style.transform = 'translateX(100px)'; // 只会引起重绘,不会引起重排
item.style.opacity = '0.5'; // 只会引起重绘,不会引起重排

2. 使用 DocumentFragment 进行批量 DOM 操作

javascript
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
  const div = document.createElement('div');
  div.textContent = 'Item ' + i;
  fragment.appendChild(div);
}
document.body.appendChild(fragment); // 一次性添加,减少重排

3. 避免复杂的 CSS 选择器

css
/* 避免 */
const item = document.querySelector('ul#list > li.item:first-child');

/* 推荐 */
const items = document.querySelectorAll('.item');
const firstItem = items[0];

4. 使用 will-change 属性

css
.item {
  will-change: transform, opacity;
}

5. 隐藏元素后再修改属性

javascript
const item = document.querySelector('.item');
item.style.display = 'none'; // 隐藏元素,之后的修改不会引起重排或重绘

// 修改元素属性
item.style.width = '200px';
item.style.height = '200px';

item.style.display = 'block'; // 显示元素,只引起一次重排和重绘

6. 减少频繁的 DOM 读取和写入

javascript
const items = document.querySelectorAll('.item');
let offsetLeft = 0;
let offsetTop = 0;

items.forEach(item => {
  offsetLeft += item.offsetLeft;
  offsetTop += item.offsetTop;
});

// 一次性更新所有元素的位置
items.forEach(item => {
  item.style.transform = `translate(${offsetLeft}px, ${offsetTop}px)`;
});

7. 使用 CSS 框架和布局技术

css
/* 使用 Flexbox 而不是 float */
.container {
  display: flex;
  flex-direction: row;
}

.item {
  flex: 1;
}

8. 避免不必要的渲染

javascript
const item = document.querySelector('.item');
item.style.display = 'none'; // 隐藏元素

// 执行复杂的 DOM 操作
item.innerHTML = '...'; // 由于元素被隐藏,这些操作不会引起重排或重绘

item.style.display = 'block'; // 显示元素,只引起一次重排和重绘