回流(Reflow)与重绘(Repaint)

131 阅读3分钟

1. 浏览器渲染流程

要理解回流和重绘,首先需要了解浏览器的渲染流程:

  1. 解析HTML:构建DOM树
  2. 解析CSS:构建CSSOM树
  3. 合并DOM和CSSOM:形成渲染树(Render Tree)
  4. 布局(Layout):计算每个节点的几何信息(位置、大小)
  5. 绘制(Paint):将渲染树绘制到屏幕上

2. 回流(Reflow)

定义

回流也称为布局(Layout),是指浏览器重新计算元素的位置和几何属性,导致渲染树的一部分或全部需要重新构建的过程。

触发条件

以下操作会触发回流:

  • 页面首次渲染
  • 浏览器窗口大小改变
  • 元素尺寸、位置、内容发生变化
  • 添加或删除可见的DOM元素
  • 激活CSS伪类(如:hover)
  • 查询某些属性或调用某些方法(如offsetWidth、getComputedStyle等)

常见触发回流的属性和方法

// 几何属性
element.offsetTop
element.offsetLeft
element.offsetWidth
element.offsetHeight
element.scrollTop
element.scrollLeft
element.scrollWidth
element.scrollHeight
element.clientTop
element.clientLeft
element.clientWidth
element.clientHeight

// 方法
element.getBoundingClientRect()
window.getComputedStyle()

3. 重绘(Repaint)

定义

重绘是指当元素的外观发生变化但不影响布局时,浏览器只需要重新绘制受影响的部分到屏幕上的过程。

触发条件

改变以下样式属性通常会触发重绘但不一定触发回流:

  • 颜色相关:color, background-color, border-color等
  • 透明度:opacity
  • 文本相关:text-decoration, text-shadow等
  • 可见性:visibility(注意与display:none的区别)
  • 轮廓:outline

4. 回流与重绘的关系

  • 回流必定引起重绘:当元素发生回流时,其外观通常会受到影响,因此浏览器需要重绘。
  • 重绘不一定引起回流:如果只是改变不影响布局的外观属性,则只发生重绘。

5. 性能影响

  • 回流比重绘代价更高:回流涉及几何计算,可能影响多个元素。
  • 现代浏览器的优化:浏览器会批量处理回流操作,但频繁访问布局属性会强制刷新队列。

6. 优化策略

减少回流次数

  1. 集中改变样式

    // 不好
    element.style.width = '100px';
    element.style.height = '200px';
    
    // 好 - 使用cssText或class
    element.style.cssText = 'width:100px; height:200px;';
    // 或
    element.className = 'new-class';
    
  2. 使用文档片段(DocumentFragment)

    const fragment = document.createDocumentFragment();
    // 添加多个元素到fragment
    document.body.appendChild(fragment);
    
  3. 批量DOM操作

    // 先使元素脱离文档流
    const el = document.getElementById('my-element');
    el.style.display = 'none';
    // 进行多次DOM操作
    el.style.display = 'block';
    
  4. 避免频繁读取布局属性

    // 不好 - 强制刷新布局队列
    const width = element.offsetWidth;
    element.style.width = width + 10 + 'px';
    const height = element.offsetHeight;
    element.style.height = height + 10 + 'px';
    
    // 好 - 先读取后写入
    const width = element.offsetWidth;
    const height = element.offsetHeight;
    element.style.width = width + 10 + 'px';
    element.style.height = height + 10 + 'px';
    
  5. 使用CSS3 transform和opacity

    • 这些属性由GPU处理,不会触发回流重绘
  6. 避免table布局

    • table中一个小改动可能导致整个table回流
  7. 使用will-change

    .element {
      will-change: transform;
    }
    

    提前告诉浏览器哪些属性会变化

  8. 使用position: absolute 、fixed

    减少对其他元素的影响

7. 现代浏览器优化

现代浏览器采用"增量回流"和"异步回流"策略:

  • 维护一个"脏位系统"标记需要回流的元素
  • 有一个回流队列,批量处理回流
  • 但某些操作(如获取布局属性)会强制刷新队列

8. 工具检测

可以使用浏览器开发者工具检测回流和重绘:

  1. Chrome DevTools → Performance面板 → 录制性能
  2. 查看"Layout"(回流)和"Paint"(重绘)事件