概念介绍
重排
重排是指浏览器重新计算元素的几何属性(位置、大小等)的过程。当 DOM 发生变化时,如元素的尺寸、位置或布局属性发生变化,浏览器需要重新计算这些属性以更新页面的布局。重排是性能开销较大的操作,因为它可能需要对页面的大部分或全部元素进行计算。
重绘
重绘是指在元素的几何属性变化后,浏览器重新绘制元素外观的过程。重绘通常比重排的开销小,因为它只影响元素的外观,不需要重新计算布局。
减少重绘和重排的原因
- 性能开销:重排和重绘是性能密集型的操作,它们会导致浏览器花费额外的时间来处理这些任务,从而影响页面的响应速度和动画的流畅度。
- 流畅度:在动画和滚动等场景中,频繁的重绘和重排会导致帧率下降,使得动画看起来卡顿,影响用户体验。
- 资源浪费:不必要的重绘和重排会浪费 CPU 和 GPU 资源,尤其是在移动设备上,这些资源更加有限。
- 内存使用:频繁的重排和重绘可能会导致内存使用增加,尤其是在处理大量 DOM 元素时。
- 响应时间:减少重绘和重排可以减少用户操作的响应时间,使得页面更加灵敏。
如何减少重绘和重排
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'; // 显示元素,只引起一次重排和重绘