虚拟DOM本质是用JavaScript对象描述真实DOM结构的轻量级抽象,而非副本;它仅含tag、data、children等属性,不保存原生节点引用或方法,通过VNode树表达渲染意图,再经diff与patch批量更新真实DOM。

虚拟 DOM 的本质,就是一个用 JavaScript 对象(Plain Object)描述真实 DOM 节点结构和行为的轻量级抽象。
它不是副本,而是“结构说明书”
很多人说虚拟 DOM 是“DOM 的副本”,这容易误解。它其实不保存任何浏览器原生节点引用,也不具备 DOM 方法(如 appendChild 或 style.color)。它只是一组约定好的属性,用来告诉 Vue:“这里该有一个什么标签、带什么属性、里面有什么内容、子节点长什么样”。比如:
- tag:字符串,表示标签名,如 "div"、"span"、"MyComponent"(组件名也算 tag)
- data:对象,存放属性、样式、事件监听器、指令等,如 { class: { active: true }, on: { click: handler } }
- children:数组,每个元素又是一个 VNode(递归结构),构成树形关系
- text:字符串,仅用于文本节点,此时 tag 为 undefined
- key:字符串或数字,用于标识节点身份,在列表渲染中决定复用逻辑
VNode 是虚拟 DOM 的基本单元
整棵虚拟 DOM 是由一个个 VNode(Virtual Node)组成的树。Vue 中的 VNode 不是 class 实例,而是一个普通 JS 对象,结构清晰、可序列化、无副作用。它的类型分五类:
- 元素节点(tag 为 HTML 标签名)
- 组件节点(tag 为组件选项或构造函数)
- 文本节点(text 存在,children 为空)
- 注释节点(isComment: true)
- 克隆节点(isCloned: true,用于 v-for 中 key 相同的复用场景)
为什么用对象,而不是直接操作 DOM?
因为真实 DOM 操作牵涉布局计算、样式重排、绘制触发,每次修改都可能引发昂贵的浏览器重排重绘。而 JavaScript 对象的创建、比对、修改成本极低。Vue 把“该渲染成什么样”这个意图,先完整表达为一棵 VNode 树,再交给 diff 和 patch 流程去决策——哪些删、哪些加、哪些改属性、哪些挪位置。整个过程都在内存中完成,不碰真实 DOM 一步,直到最后批量提交。
立即学习“Java免费学习笔记(深入)”;
一个简单例子帮你理解结构映射
下面这段模板:
<div class="box" @click="handle">Hello <span>World</span></div>
会被编译/渲染成类似这样的 VNode 对象(简化版):
{
tag: "div",
data: { class: "box", on: { click: handle } },
children: [
{ text: "Hello " },
{ tag: "span", children: [{ text: "World" }] }
]
}
这个对象本身不会渲染任何东西,但它完整表达了最终要生成的 DOM 结构和交互意图。


















