哪些操作会触发回流 (Reflow)?如何减少回流?
回流 (Reflow),也称为重排,是指浏览器为了重新计算文档中元素的位置和几何形状(大小),需要重新构建渲染树(Render Tree)的一部分或全部的过程。
回流的成本比重绘(Repaint)高得多,因为它涉及到布局的重新计算。回流必然导致重绘,但重绘不一定导致回流。
以下是触发回流的操作以及减少回流的策略:
一、 哪些操作会触发回流?
只要涉及到几何尺寸、布局位置、文档结构的变化,都会触发回流。
1. DOM 结构的变化
- 添加或删除可见的 DOM 元素。
- 元素位置改变。
- 元素尺寸改变(包括外边距
margin、内边距padding、边框厚度border、宽度width、高度height等)。
2. 内容的变化
- 文本内容改变(例如输入框打字)。
- 图片加载完成(如果图片大小未固定,加载完会撑开容器)。
- 字体大小 (
font-size) 或字体系列 (font-family) 改变。
3. 视口(Viewport)的变化
- 浏览器窗口大小调整(Resize)。
- 滚动条的出现或消失。
4. CSS 样式的变化
- 激活 CSS 伪类(例如
:hover)。 - 设置
style属性的值。 - 关键点:
display: none会触发回流(因为它从渲染树中移除了),而visibility: hidden只触发重绘(因为它占据空间,只是不可见)。
5. 获取某些属性值(强制同步布局)
这是最容易被忽视的一点。当你请求以下属性时,浏览器为了返回最新的准确值,必须清空队列并立即执行回流:
offsetTop,offsetLeft,offsetWidth,offsetHeightscrollTop,scrollLeft,scrollWidth,scrollHeightclientTop,clientLeft,clientWidth,clientHeightgetComputedStyle()(或 IE 的currentStyle)getBoundingClientRect()
二、 如何减少回流?
核心思想是:减少对渲染树的操作次数 和 减小回流的影响范围。
1. CSS 优化
- 集中修改样式: 不要一条条地修改 DOM 的
style属性,而是通过修改class属性或csstext一次性应用所有样式。javascript// ❌ 糟糕的写法 (触发多次回流) el.style.width = '10px'; el.style.height = '10px'; // ✅ 推荐写法 el.classList.add('new-style'); - 使用 Transform 和 Opacity: 尽可能使用
transform代替top/left做位移,使用opacity代替visibility。在支持硬件加速的浏览器中,这两个属性可以触发 GPU 合成(Composite),跳过回流和重绘阶段。 - 避免使用 Table 布局: Table 布局通常需要多次计算才能确定最终布局,且 Table 中任何一个单元格的变动都可能导致整个 Table 的回流。
- 避免多层内联样式: 样式层级越深,回流成本越高。
2. DOM 操作优化
- 离线操作 DOM (Batching):
- 使用
documentFragment创建一个文档片段,在片段上进行多次 DOM 操作,最后一次性添加到文档中。 - 先将元素设为
display: none(触发一次回流),进行多次修改,再设回display: block(再触发一次回流)。这样中间的修改不会触发回流。 - 使用
cloneNode克隆节点,修改完副本后,替换旧节点。
- 使用
- 避免频繁读取布局信息(读写分离):
不要在循环中交替读取和写入布局属性(这会导致“布局抖动” Layout Thrashing)。应该先读取所有需要的值,保存下来,然后再统一写入。javascript// ❌ 糟糕的写法 (读-写-读-写,强制多次回流) for (let i = 0; i < els.length; i++) { els[i].style.width = box.offsetWidth + 'px'; } // ✅ 推荐写法 (读全部 -> 写全部) const width = box.offsetWidth; for (let i = 0; i < els.length; i++) { els[i].style.width = width + 'px'; }
3. 动画优化
- 脱离文档流: 对需要频繁动画的元素使用绝对定位 (
position: absolute或fixed)。这样它们脱离了标准文档流,回流时只会影响自身,不会导致父元素或后续元素的级联回流。 - 使用
requestAnimationFrame: 让浏览器在下一次重绘之前更新动画,保证流畅度。
4. 现代浏览器特性
- CSS
will-change: 可以提前告诉浏览器某个元素将要发生变化(如will-change: transform),浏览器会为其分配特定的层(Layer),从而优化渲染。但不要滥用,否则会占用大量内存。
总结
回流是昂贵的。优化的关键在于:批量修改、脱离文档流、利用 GPU 加速、避免强制同步布局。