浏览器的重绘 (Repaint) 和回流 (Reflow/Layout)的区别?
浏览器的 重绘 (Repaint) 和 回流 (Reflow,也称重排/Layout) 是前端性能优化中非常核心的概念。理解它们的区别有助于写出性能更好的页面。
简单的一句话总结:回流一定会触发重绘,但重绘不一定会触发回流。
1. 概念定义
为了理解这两个概念,首先需要知道浏览器渲染页面的简要流程:
- 解析 HTML 生成 DOM 树。
- 解析 CSS 生成 CSSOM 树。
- 将 DOM 和 CSSOM 合并生成 渲染树 (Render Tree)。
- 回流 (Layout/Reflow):根据渲染树,计算每个节点在屏幕上的几何信息(位置、大小)。
- 重绘 (Painting):根据计算好的几何信息,将节点的像素(颜色、背景等)绘制到屏幕上。
- 合成 (Composite):将多个层合成最终图像。
回流 (Reflow / Layout)
当渲染树中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
- 核心: 涉及几何信息(位置、大小)的改变。
- 比喻: 就像你在房间里重新摆放家具,你移动了沙发,可能连带茶几也要移动,整个房间的布局都变了。
重绘 (Repaint)
当元素样式的改变不影响它在文档流中的位置(如颜色、背景色、边框颜色等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
- 核心: 涉及外观(颜色、风格)的改变,不影响布局。
- 比喻: 就像给沙发换了个红色的套子,沙发的位置和大小没变,房间布局也没变,只是样子变了。
2. 详细区别对比
| 特性 | 回流 (Reflow) | 重绘 (Repaint) |
|---|---|---|
| 触发条件 | 元素的几何属性(宽、高、位置)或结构发生变化。 | 元素的外观属性(颜色、背景)发生变化,但几何属性未变。 |
| 性能消耗 | 非常高。可能导致整个页面重新布局。 | 较低。只改变像素颜色,不涉及几何计算。 |
| 相互关系 | 回流必将引起重绘。 | 重绘不一定引起回流。 |
| 影响范围 | 全局(整个页面)或 局部(某个 DOM 分支)。 | 仅受影响的元素本身。 |
3. 常见触发场景
触发回流 (Reflow) 的操作(代价大)
只要涉及“动了位置”或“变了大小”,就会回流:
- 页面首次渲染(无法避免)。
- 浏览器窗口大小发生改变(Resize)。
- 元素尺寸或位置发生改变(
width,height,padding,margin,left,top,border)。 - 元素内容变化(文字数量变化、图片大小变化、输入框输入文字)。
- 元素字体大小变化(
font-size)。 - 添加或者删除可见的 DOM 元素。
- 激活 CSS 伪类(如
:hover)。 - 查询某些属性或调用某些方法(强制同步布局):
offsetWidth,offsetHeight,clientWidth,clientHeight,scrollTop,scrollHeight等。getComputedStyle()。- 原因:浏览器为了返回最新的值,必须强制立即执行一次回流。
触发重绘 (Repaint) 的操作(代价小)
只涉及“样子变了”:
colorbackground-color/background-imagevisibility(注意:visibility: hidden只是看不见,位置还在,所以是重绘;而display: none彻底消失,会触发回流)outlinebox-shadow
4. 性能优化策略
既然回流的代价很高,我们需要尽量避免或减少回流。
避免频繁操作 DOM
- 使用
DocumentFragment创建一个文档碎片,在它上面批量操作 DOM,最后一次性添加到文档中。 - 或者先
display: none隐藏元素(触发一次回流),进行多次修改,再display: block显示(再触发一次回流)。
- 使用
避免频繁读取布局信息(读写分离)
- 错误写法(导致多次回流):javascript
// 读一次,写一次,浏览器被迫不断重排 div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px'; - 正确写法(缓存布局信息):javascript
// 先读完 const curLeft = div.offsetLeft; const curTop = div.offsetTop; // 再统一写 div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px';
- 错误写法(导致多次回流):
使用 CSS 类合并样式修改
- 不要一条条修改
style属性,而是定义一个 CSSclass,一次性切换类名。
- 不要一条条修改
使用
transform和opacity实现动画- 这是现代前端优化的关键点。
- 修改
transform(位移、缩放) 和opacity属性,在某些浏览器中会触发硬件加速 (GPU),既不会触发回流,甚至可能不会触发重绘(直接在合成层处理),性能极高。
将复杂的动画元素脱离文档流
- 给动画元素设置
position: absolute或fixed。这样它在回流时,只会影响它自己,不会推挤周围的元素,减少回流的影响范围。
- 给动画元素设置
总结
- 回流是布局计算,重绘是像素绘制。
- 回流成本远高于重绘。
- 优化目标:减少回流次数,缩小回流范围。