Vue 虚拟 DOM:从浏览器渲染到性能优化
浏览器渲染流程:DOM + CSSOM → Render Tree → Layout → Paint → Composite重排开销大,重绘次之连续 DOM 修改 + 不读取布局属性 → Layout 只计算一次连续 DOM 修改 + 中间读取布局属性 → Layout 多次计算 → 性能下降虚拟 DOM:先在内存 diff → 最小 patch → 减少同步 Layout → 页面更新流畅。
·
Vue 虚拟 DOM:从浏览器渲染到性能优化的逐步解析
当页面有一个长列表或复杂表格,JS 一次性渲染完后,浏览器突然卡顿,滚动不流畅?为什么会这样?要理解这个问题,我们需要先了解 浏览器是如何渲染页面的。
一、浏览器渲染流程
浏览器渲染流程主要分为以下阶段:
- DOM 树构建:解析 HTML,生成 DOM 节点对象
- CSSOM 树构建:解析 CSS,计算每个元素的样式
- Render Tree:结合 DOM + CSSOM,只包含可见节点和样式信息
- Layout(重排 / Reflow):计算节点最终位置和尺寸
- 触发条件:尺寸、位置、布局属性变化
- Paint(重绘 / Repaint):绘制元素像素
- 触发条件:颜色、背景、字体颜色变化
- Composite(合成):GPU 图层合成最终画面
- 触发条件:transform、opacity 等 GPU 属性变化
JS 主线程和渲染线程互斥,JS 执行期间渲染线程被阻塞,渲染通常在 JS 执行完成后统一进行。
二、重排与重绘触发机制
| 操作类型 | 是否触发重排 | 是否触发重绘 | 说明 |
|---|---|---|---|
| 改变元素尺寸 / 位置 / 布局属性 | ✅ | ✅ | Layout 阶段重新计算,开销大,可能闪烁 |
| 改变颜色 / 背景 / 字体颜色 | ❌ | ✅ | 只绘制,不重排,开销中等 |
| transform / opacity | ❌ | ❌ | GPU 图层处理,开销小 |
| 读取布局属性(offsetWidth / offsetHeight / getBoundingClientRect) | ✅(同步 Layout) | ❌ | 强制计算 Layout,但不重新生成 Render Tree,Paint 延迟 |
三、直接操作 DOM 的性能差异
1️⃣ 不读取布局属性
const el1 = document.getElementById('el1');
const el2 = document.getElementById('el2');
const el3 = document.getElementById('el3');
el1.style.width = '100px';
el2.style.width = '150px';
el3.style.width = '200px';
执行过程:
- JS顺序修改内存DOM属性
- 浏览器标记节点为dirty(需Layout/Paint)
- JS执行完毕后,渲染线程统一执行Render Tree -> Layout -> Paint
效果
- 视觉上直接显示最终状态 (200px)
- 内部Layout只计算一次,减少CPU开销
核心结论
- 连续修改DOM,但不读取布局属性,渲染性能最佳
2️⃣ 中间读取布局属性(性能陷阱)
el1.style.width = '100px';
console.log(el1.offsetWidth); // 读取布局属性,强制同步 Layout
el2.style.width = '150px';
console.log(el2.offsetWidth);
el3.style.width = '200px';
console.log(el3.offsetWidth);
执行过程
- JS 修改 DOM 属性 → 标记节点 dirty
- 读取布局属性(offsetWidth 等) → 浏览器必须立即同步计算 Layout(重排)
- 注意:不会重新生成 Render Tree
- Paint(重绘)仍延迟到 JS 执行完成或下一帧
- 每次读取都会触发一次同步 Layout
效果
- 视觉上最终显示 200px
- 内部 Layout 被计算三次
核心结论
- 读取布局属性会触发 同步 Layout(重排)
- 不会重新生成 Render Tree
- 不会立即触发 Paint(重绘)
- 多次读取布局属性 → 多次同步 Layout → 性能下降
四、虚拟 DOM 的出现
Vue 提出了 虚拟 DOM(Virtual DOM) 来解决频繁 DOM 操作导致的性能问题。
- 在内存中创建虚拟 DOM 树(VNode)
- 描述 DOM 节点及属性,只在 JS 内存中操作
- 修改虚拟 DOM 不触发浏览器渲染
- Diff 算法计算新旧虚拟 DOM 差异
- 找出真正变化的节点(新增、删除、属性变化)
- 生成最小 DOM 更新操作
- Patch 到真实 DOM
- 避免中间多次同步 Layout
- 页面更新更流畅,CPU 消耗低
核心:先在 JS 内存计算差异,再批量更新 DOM,减少同步 Layout 次数
五、总结
- 浏览器渲染流程:DOM + CSSOM → Render Tree → Layout → Paint → Composite
- 重排开销大,重绘次之
- 连续 DOM 修改 + 不读取布局属性 → Layout 只计算一次
- 连续 DOM 修改 + 中间读取布局属性 → Layout 多次计算 → 性能下降
- 虚拟 DOM:先在内存 diff → 最小 patch → 减少同步 Layout → 页面更新流畅
一句话理解:虚拟 DOM 并不是让 DOM 本身更快,而是 减少浏览器内部重复 Layout 计算,提高页面渲染效率。
更多推荐


所有评论(0)