【Web前端】重绘(Repaint)与回流/重排(Reflow)详解
1. 浏览器的渲染流程
1)解析HTML,构建DOM树;
2)解析CSS,构建CSSOM树;
3)将DOM树和CSSOM树合并,生成渲染树(Render Tree),渲染树只包含可见节点及其样式信息;
4)布局/回流:计算每个可见节点在视口中的精确位置和大小;
5)绘制/重绘:将渲染树中的每个节点转换成屏幕上的实际像素,包括颜色、边框、阴影等。
2. 什么是回流/重排(Reflow)?
回流/重排是指当渲染树中的一部分(或全部)因为元素的规模、尺寸、结构、位置等发生变化,需要重新计算每个节点在视口中的几何属性(位置、尺寸)的过程。
触发回流的常见操作:
1)添加或删除可见的DOM元素。
2)元素位置发生变化(如 position、top、left 等)。
3)元素尺寸发生变化(如 width、height、padding、border、margin)。
4)内容改变(例如文本内容变化,或图片被不同尺寸的图片替换)。
5)浏览器窗口尺寸改变(resize 事件)。
6)激活CSS伪类(如 :hover)。
7)查询某些属性或方法,这些属性需要实时计算的几何信息,例如:
① offsetTop、offsetLeft、offsetWidth、offsetHeight
② scrollTop、scrollLeft、scrollWidth、scrollHeight
③ clientTop、clientLeft、clientWidth、clientHeight
④ getComputedStyle() 或 currentStyle
回流会重新计算布局,成本很高,因为它可能影响整个文档的部分或全部。
3. 什么是重绘(Repaint)?
重绘是指当元素的外观(如颜色、背景、可见性等)发生变化,但不会影响其几何属性,浏览器重新绘制该元素的过程。
触发重绘的常见操作:
1)改变元素的 color、background-color、border-color、box-shadow、outline 等外观属性。
2)改变 visibility(但 display: none 会触发回流,因为元素从文档流移除)。
3)改变 opacity(在某些情况下可能触发硬件加速合成,不一定会重绘)。
重绘的代价相对较小,但仍然需要遍历渲染树进行绘制。
4. 回流与重绘的关系
1)回流必定导致重绘(因为位置变了,需要重新绘制)。
2)重绘不一定导致回流(如只改颜色)。
5. 如何避免和优化回流与重绘
为了提升页面性能,应尽量减少回流和重绘的次数,或者将它们的负面影响降到最低。
5.1 减少直接操作DOM和样式
1)批量修改样式:避免逐条修改样式,改用 class 或一次性设置 style.cssText。
// 不推荐
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// 推荐
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// 或使用
element.classList.add('new-style');
2)批量操作DOM:将多个操作合并在一起,减少对文档的频繁修改。
① 使用 DocumentFragment 在内存中构建子节点,然后一次性添加到DOM。
② 先隐藏元素(display: none),进行一系列操作后再显示。隐藏期间触发的回流只发生一次。
const ul = document.getElementById('list');
ul.style.display = 'none';
// 批量添加大量 li
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
ul.appendChild(li);
}
ul.style.display = 'block';
③ 使用 cloneNode 克隆一个节点,在克隆体上操作,然后替换原节点。
5.2 避免频繁读取引起布局抖动的属性
缓存需要多次读取的布局属性值,避免强制浏览器在每次读取时重新计算布局(即“强制同步布局”)。
// 不推荐——每次循环都触发回流
for (let i = 0; i < 100; i++) {
console.log(div.offsetWidth);
}
// 推荐——缓存一次
const width = div.offsetWidth;
for (let i = 0; i < 100; i++) {
console.log(width);
}
5.3 优化动画效果
1)使用 transform 代替 top/left 等位置属性。transform 不会触发回流和重绘,而是由合成器处理,性能更优。
/* 不推荐 */
.box {
animation: move 1s infinite;
}
@keyframes move {
0% { left: 0; }
100% { left: 100px; }
}
/* 推荐 */
.box {
transform: translateX(0);
animation: move 1s infinite;
}
@keyframes move {
0% { transform: translateX(0); }
100% { transform: translateX(100px); }
}
2)使用 opacity 配合 will-change,将动画元素提升到独立的合成层(浏览器会为其创建单独的层,减少重绘区域)。
.animated {
will-change: transform, opacity;
}
3)对于高频事件(如 scroll、resize、mousemove),使用防抖(debounce)或节流(throttle)来减少触发频率。
window.addEventListener('resize', throttle(() => {
// 执行操作
}, 100));
5.4 使用现代CSS布局技术
1)尽量使用 flexbox 或 grid 布局,它们在计算回流时通常比传统的 float 或定位方式更高效。
2)避免使用表格布局(table),因为即使一个单元格发生变化,也可能导致整个表格重新布局。
5.5 将频繁变化的元素提升到独立图层
除了 will-change,还可以使用 transform: translateZ(0) 或 backface-visibility: hidden 来强制创建新层。但不要过度使用,因为创建图层也会消耗内存。
5.6 其他细节
1)对于复杂动画元素,使用 position: fixed 或 absolute 将其脱离文档流,减少对其他元素的影响。
2)在需要动态修改样式时,尽量减少影响范围(例如通过修改父元素来统一改变子元素,而不是逐个修改)。
6. 总结
1)回流成本远高于重绘,应重点避免不必要的回流。
2)核心优化思路:减少布局计算次数、避免强制同步布局、利用合成器优势、批量操作DOM。
3)通过合理使用现代CSS属性(transform、opacity、will-change)和JavaScript技巧,可以显著提升页面渲染性能。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)