Vue.js虚拟DOM通过响应式触发、VNode生成、Diff比对、批量patch四环节协同实现高效更新:响应式系统捕获变更并异步队列更新;VNode树描述新界面;Diff算法同层双端比对定位最小变更;最终批量提交DOM操作。

Vue.js 的虚拟 DOM 不是“替代 DOM”,而是用轻量级 JavaScript 对象描述真实 DOM 结构,再通过一套闭环机制把数据变化精准、最小化地反映到界面上。它的高效不靠单点炫技,而靠响应式触发、VNode 生成、Diff 比对、批量 patch 四个环节的紧密咬合。
响应式系统决定“什么时候更新”
当你修改 data 或 ref 中的值,Vue 的 getter/setter(或 Proxy)立刻捕获变更,并通知所有依赖该值的 Watcher(比如用了 {{ msg }} 的组件)。这些 Watcher 被标记为“待更新”,但不会立刻操作 DOM——它们被推入一个异步更新队列,等待下一个微任务(nextTick)统一执行。
- 同一事件循环中多次赋值(如连续 3 次
this.count++)只触发一次重新渲染 - 避免了频繁触发重排重绘,也防止了中间状态闪现
- 开发者可用
nextTick(() => { /* DOM 已更新 */ })精确获取更新后的视图
VNode 树负责“怎么描述新界面”
被唤醒的 Watcher 会调用组件的 render 函数,生成一棵全新的虚拟 DOM 树(VNode 树)。这棵树纯属内存计算,不含任何真实 DOM 操作:
- 每个 VNode 是普通 JS 对象,含
tag、props、children、key等字段 - 静态节点(如未绑定插值的
<h1>标题</h1>)会被标记为static,跳过后续 diff - 列表渲染时,
v-for必须带key,否则 Vue 无法识别节点身份,只能就地复用,易导致状态错乱
Diff 算法专注“哪里需要改”
新旧两棵 VNode 树进入 patch 阶段,Vue 的 Diff 不做全量递归比对,而是采用同层比较 + 双端指针策略,快速定位最小变更集:
立即学习“前端免费学习笔记(深入)”;
- 跨层级变动(如父节点被删)直接整块替换,不浪费时间逐层对比
- 同层节点先比
key,再比标签名;key不同或标签不同,直接销毁重建 - 子节点更新用四步双端比对:头-头、尾-尾、旧头-新尾、旧尾-新头,尽可能复用已有节点并移动而非新建
- 文本节点只比
text字段;元素节点比class、style、props和子结构
真实 DOM 更新走“批量提交”路径
Diff 完成后,得到的是一组精细的 DOM 操作指令(如“插入第 3 个子节点”“更新 div 的 class 属性”),这些指令被合并、排序,最后一次性批量应用到真实 DOM:
- 避免反复触发浏览器 layout/paint 流程
- 对同一个元素的多次属性修改(如同时改
class和style)只触发一次重绘 - 即使组件内部触发 10 次数据变更,最终也只产生一次 DOM 批量更新


















