基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

哪些操作会触发回流 (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, offsetHeight
  • scrollTop, scrollLeft, scrollWidth, scrollHeight
  • clientTop, clientLeft, clientWidth, clientHeight
  • getComputedStyle() (或 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: absolutefixed)。这样它们脱离了标准文档流,回流时只会影响自身,不会导致父元素或后续元素的级联回流。
  • 使用 requestAnimationFrame 让浏览器在下一次重绘之前更新动画,保证流畅度。

4. 现代浏览器特性

  • CSS will-change 可以提前告诉浏览器某个元素将要发生变化(如 will-change: transform),浏览器会为其分配特定的层(Layer),从而优化渲染。但不要滥用,否则会占用大量内存。

总结

回流是昂贵的。优化的关键在于:批量修改、脱离文档流、利用 GPU 加速、避免强制同步布局。

00:00
00:00