如何在前端避免重排(Reflow)和重绘(Repaint)
重排(Reflow)和 重绘(Repaint)是浏览器在处理页面渲染时的两大性能开销,频繁的重排和重绘会导致页面响应变慢、卡顿,影响用户体验。因此,减少和避免重排和重绘是前端优化的关键。
1. 避免重排(Reflow)
重排发生在当页面布局变化时,例如修改元素的尺寸、位置或在 DOM 中新增、删除元素。这是最消耗性能的操作之一。
1.1. 减少DOM操作
DOM 操作是引发重排的主要原因之一。尽量减少频繁的 DOM 结构变化,使用批量操作技术来减少性能损耗。
- 方法:使用
DocumentFragment
或者一次性更新 DOM,避免逐个操作 DOM。
let fragment = document.createDocumentFragment();
let div = document.createElement('div');
fragment.appendChild(div);
document.body.appendChild(fragment);
1.2. 避免使用特定属性
有些属性会强制触发重排,例如:
offsetWidth
offsetHeight
scrollTop
clientWidth
在读取这些属性时,浏览器会立即重新计算页面布局,导致性能下降。避免频繁读取这些属性,或者缓存它们的值。
1.3. 使用CSS动画而非JavaScript
尽量使用 CSS 动画 和 过渡效果 来代替 JavaScript 的样式变化,因为 CSS3 动画利用硬件加速,可以避免频繁触发重排。
1.4. 利用 position: absolute
或 position: fixed
对于不需要参与页面文档流的元素,使用 position: absolute
或 position: fixed
,减少其影响其他元素的重排。
1.5. 使用 display: none
隐藏元素时,使用 display: none
,它不会占据空间,也不会导致重排;相比之下,visibility: hidden
仅隐藏元素,但它仍然占据空间,可能引发页面重排。
1.6. 使用 requestAnimationFrame
将多个 DOM 操作放在 requestAnimationFrame
中,使浏览器可以在下一帧之前批量处理所有变更,减少中间无效的重排。
requestAnimationFrame(() => {
// 批量处理 DOM 操作
});
2. 避免重绘(Repaint)
重绘是当元素外观发生变化时(如颜色、背景图像变化)浏览器需要重新绘制的过程,虽然它比重排消耗小,但频繁的重绘也会影响性能。
2.1. 使用 will-change
属性
will-change
告诉浏览器即将发生变化的样式,提前做好优化准备,减少不必要的重绘。
.element {
will-change: transform;
}
2.2. 使用合成层
通过使用 translateZ(0)
或 translate3d(0, 0, 0)
等属性将元素移入 GPU 加速的合成层,这样能将它们从主文档流中分离出来,避免影响其他元素。
.element {
transform: translateZ(0);
}
2.3. 使用离屏Canvas
对于复杂的图形操作,可以先在离屏 Canvas 中绘制,再将结果绘制到屏幕上,避免频繁的重绘。
2.4. 使用 display: contents
display: contents
可以使元素只作为 DOM 容器存在,而不会在渲染树中产生任何视觉边界,从而减少不必要的重绘。
2.5. 使用 CSS 变量
使用 CSS 变量 可以减少重绘开销,尤其是在需要频繁改变样式时。
2.6. 避免昂贵的样式属性
尽量避免使用如 clip-path
、filter
和 mix-blend-mode
等性能开销大的属性,因为它们可能导致大量重绘。
3. 总结
为了避免重排和重绘,以下几条策略非常关键:
- 批量操作 DOM,尽量减少 DOM 操作次数。
- 使用合成层,将频繁变化的元素放到单独的合成层。
- 使用
will-change
提前通知浏览器进行优化。 - 减少使用昂贵的 CSS 属性,例如
filter
和clip-path
。
通过这些优化技术,可以显著提高页面性能和用户体验。