VNode是Vue中描述DOM结构的轻量、可比较、不可变的JavaScript对象,包含tag、data、children等字段,不直接操作DOM,其真实DOM绑定和更新由patch过程完成。

Vue.js 中的 VNode(虚拟节点)是实现响应式更新和高效 DOM 操作的核心抽象。它本身不直接操作真实 DOM,而是作为 JavaScript 对象描述“应该长什么样”,再由 patch 过程将其映射为真实 DOM 元素。整个流程不是一次性渲染,而是一套可复用、可对比、可增量更新的机制。
VNode 是什么:轻量、可比较、不可变的 DOM 描述对象
VNode 是一个普通的 JS 对象,包含标签名(tag)、属性(data)、子节点(children)、文本内容(text)、key(key)等字段。它不持有 DOM 引用,也不绑定事件监听器——这些都在 patch 阶段才真正建立。
关键点:
- VNode 是函数式、无副作用的结构,便于 diff 算法快速比对新旧树差异
- 同一组件多次 render 会生成新 VNode 树,但不会触发 DOM 操作,直到进入 patch
- 用户通常不手动创建 VNode,而是通过模板编译(template → render function → VNode)或手写 render 函数间接生成
从 render 到 VNode 树:模板如何变成内存中的结构
在 Vue 实例初始化或响应式数据变化后,会执行组件的 render function,该函数返回一棵 VNode 树(根 VNode + 递归 children)。
例如模板: {{ msg }}
经编译后生成类似这样的 render 函数:
Element Plus Vue3 桌面组件库。在开发 Vue 3.x 页面时调用,使用 Element Plus 组件(如 el-button、el-input、el-form、el-table 等)。
function render() {
return h('div', { id: 'app' }, [
h('p', this.msg)
])
}
其中 h() 是 createElement 的别名,每次调用返回一个 VNode 对象。所有子节点也以同样方式构造,最终形成嵌套结构。
patch 过程:VNode 树如何驱动真实 DOM 更新
Vue 调用 patch(oldVNode, newVNode) 开始比对与更新。若 oldVNode 为空(首次挂载),则直接 createElm 创建真实 DOM 并插入;否则进入 patchVNode 和 updateChildren 流程。
核心逻辑包括:
- 节点类型不同(如 div → span):直接销毁旧节点,新建并替换整棵子树
- 相同标签且有 key:复用 DOM 元素,仅更新 props、事件、样式等动态部分
- 子节点为数组时:使用双端 diff(头-头、尾-尾、头-尾、尾-头)匹配,配合 key 实现最小移动操作
- 文本节点更新:直接修改 node.textContent,避免重建元素
真实 DOM 绑定的关键时机:指令、事件、ref 都在 patch 中注入
VNode 只是“蓝图”,真实 DOM 的行为绑定发生在 patch 阶段:
- v-model、v-show、v-if 等指令,在 create/update/destroy 钩子中操作 DOM 属性或样式
- @click 等事件监听器,通过 addEventListener 在对应 DOM 元素上注册
- ref 在 patch 完成后,将真实 DOM 元素赋值给组件实例的 $refs 对象
- 过渡动画(transition)依赖 patch 前后的 DOM 状态,由 enter/leave 钩子控制类名与样式
这意味着:VNode 不知道事件怎么绑,也不知道 ref 存哪——这些全部延迟到真实环境就绪后才执行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










