浏览器的 Reflow 和 Repaint 是什么?为什么要尽量避免它们?

在前端性能优化的讨论中,Reflow 和 Repaint 是两个绕不开的概念。理解它们,才能真正明白为什么 Vue 要做异步批量更新,为什么频繁操作 DOM 会让页面卡顿。

1、浏览器是怎么把页面画出来的

先简单了解浏览器渲染页面的流程:

css 复制代码
HTML + CSS 解析
      ↓
构建 DOM 树 + CSSOM 树
      ↓
合并成 Render Tree(渲染树)
      ↓
Layout(布局/排版)← 这一步就是 Reflow
      ↓
Paint(绘制)← 这一步就是 Repaint
      ↓
Composite(合成,显示到屏幕)

页面首次加载时,这个流程走一遍。之后每次你修改 DOM 或 CSS,浏览器需要从某个步骤重新开始,这就是性能开销的来源。

2、Reflow(重排/重新布局)

定义 :当页面中元素的几何信息(位置、尺寸)发生变化时,浏览器需要重新计算所有受影响元素的布局。

2.1 什么会触发 Reflow

  • 修改元素的宽高、边距、边框
  • 增加/删除 DOM 元素
  • 修改字体大小
  • 窗口 resize
  • 读取某些 DOM 属性(offsetWidthgetBoundingClientRect() 等)

2.2 为什么 Reflow 代价大

Reflow 是级联的。一个元素的尺寸变了,它的兄弟元素、父元素、子元素都可能需要重新计算位置。在复杂页面里,一次 Reflow 可能需要重新计算几百上千个元素的布局。

打个比方:你在一个书架上抽掉了一本书,旁边所有的书都会滑动重新排列------书架越大,需要重排的书越多。

3、Repaint(重绘)

定义 :当元素的视觉样式改变(但不影响布局)时,浏览器需要重新绘制该元素。

3.1 什么会触发 Repaint(但不触发 Reflow)

  • 改变颜色(colorbackground-color
  • 改变透明度(opacity
  • 改变阴影(box-shadow
  • 改变轮廓(outline

3.2 Reflow 和 Repaint 的关系

Reflow 一定会触发 Repaint (布局变了,肯定要重画)。

Repaint 不一定触发 Reflow(只是颜色变了,位置没动)。

所以 Reflow 的代价 > Repaint 的代价。

4、用代码感受一下

例 1:触发多次 Reflow(性能差)

ini 复制代码
const box = document.querySelector('.box')

box.style.width = '200px'   // 触发 Reflow ①
box.style.height = '200px'  // 触发 Reflow ②
box.style.margin = '20px'   // 触发 Reflow ③

每行都触发一次 Reflow,共 3 次。

例 2:批量修改,只触发一次 Reflow(性能好)

rust 复制代码
// 方法一:一次性设置 class
box.className = 'box box--large'  // 只触发 1 次 Reflow

// 方法二:用 cssText 批量设置
box.style.cssText = 'width: 200px; height: 200px; margin: 20px'  // 只触发 1 次 Reflow

例 3:读取 DOM 属性会强制触发 Reflow

arduino 复制代码
const box = document.querySelector('.box')

box.style.width = '200px'
// 以下读取操作会强制浏览器立刻完成未处理的 Reflow,才能返回准确值
const width = box.offsetWidth   // 强制 Reflow
const height = box.offsetHeight // 再次强制 Reflow

box.style.height = width + 'px' // 又触发一次 Reflow

这个"写 → 读 → 写 → 读"的交替模式是 Reflow 最常见的陷阱,叫做 Layout Thrashing(布局抖动)

优化方式:先把要读的值都读完,再统一写:

arduino 复制代码
// ✅ 先读
const width = box.offsetWidth
const height = box.offsetHeight

// 再写
box.style.width = (width + 10) + 'px'
box.style.height = (height + 10) + 'px'
// 只触发 1 次 Reflow

5、哪些操作完全不触发 Reflow 和 Repaint

有些 CSS 属性由 GPU 处理,完全绕过 Reflow 和 Repaint:

  • transform(平移、缩放、旋转)
  • opacity(部分情况下)
  • filter

这也是为什么做动画时,推荐用 transform: translateX() 代替直接修改 left 值------前者不触发 Reflow,后者触发。

css 复制代码
/* ❌ 性能差:触发 Reflow */
.box {
  transition: left 0.3s;
}

/* ✅ 性能好:GPU 处理,不触发 Reflow */
.box {
  transition: transform 0.3s;
}

6、回到 Vue:为什么批量更新这么重要

现在再看 Vue 的异步批处理,逻辑就很清晰了:

kotlin 复制代码
// Vue 同步更新(假设):改 3 次数据 → 3 次 Reflow
this.width = 200
this.height = 200
this.visible = true

// Vue 异步批处理(实际):改 3 次数据 → 合并 → 1 次 Reflow

Vue 的异步更新本质上就是在帮你避免 Layout Thrashing,把多次 DOM 修改合并成一次,只触发一次 Reflow + Repaint。

7、总结

Reflow Repaint
触发条件 几何信息变化(尺寸、位置) 视觉样式变化(颜色、阴影)
代价 重新计算整个页面布局,代价大 只重绘元素外观,代价相对小
是否连带触发另一个 Reflow 必然触发 Repaint Repaint 不触发 Reflow
如何减少 批量修改 DOM、使用 transform 做动画 使用 transform/opacity 代替触发 Repaint 的属性

记住一句话:Reflow 和 Repaint 本身不可避免,但可以通过批量操作来减少触发次数------这也是 Vue 异步更新 DOM 的根本动机。

相关推荐
梦帮科技2 小时前
GRAVIS v4.0:基于Web的极速套利架构设计与实时数据流实现
前端·人工智能·rust·自动化·区块链·智能合约·数字货币
爱勇宝3 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
微信开发api-视频号协议3 小时前
企业微信外部群开发自动化实践过程
java·前端·微信·自动化·企业微信·ipad
甲维斯3 小时前
Fable5:20美金的顶级设计师!
前端·人工智能
kyriewen4 小时前
我同时用了 Claude Code、Cursor 和 Codex,说说三个工具的真实差距——2026 年选错工具比不用 AI 更浪费时间
前端·ai编程·claude
Jackson__4 小时前
为了拯救我的腰椎和颈椎,我做了款浏览器插件!
前端·javascript·开源
闲猫4 小时前
Python FastAPI + SQLAlchemy 入门教程:从零搭建你的第一个 Web 应用
前端·python·fastapi
林小帅4 小时前
NestJS v11 + Prisma v7 ESM 迁移配置解析
前端·javascript·后端
IT_陈寒5 小时前
React的setState竟然不是立刻生效的,害我调试半天
前端·人工智能·后端
简简单单就是我_hehe5 小时前
前端埋点日志解决方案:完整 User-Agent 解析工具实现,爬虫识别、设备品牌、操作系统、浏览器版本全解析(附完整源码)
前端·爬虫