VNode 的 el 属性在挂载阶段动态赋值,是真实 DOM 引用而非固有属性;它支撑 diff/patch 的精准操作,确保响应式更新与 DOM 映射一致。

VNode 中的 el 属性不是一开始就存在的,而是在挂载(mount)阶段被动态赋值的真实 DOM 元素引用。它之所以是“动态绑定”的,根本原因在于:虚拟节点本身是不可变的快照,而真实 DOM 是运行时可变的实体,二者必须在恰当的时机建立一对一映射,才能支撑后续的 diff 与 patch。
el 是挂载过程的产物,不是 VNode 的固有属性
VNode 创建时只描述“应该长什么样”——比如标签名、props、子节点、key 等,但不包含对真实 DOM 的任何引用。只有当 Vue 执行 patch 过程,把 VNode 渲染成真实节点后,才会把生成的 DOM 元素赋给该 VNode 的 el 字段。这个动作发生在首次渲染或更新过程中,是副作用驱动的,不是声明式定义的一部分。
- 初次挂载时:
createElm(vnode)创建真实 DOM,并将返回的 element 赋给vnode.el - 更新时:若复用旧节点,会把已有
el复制到新 VNode 上;若替换,则重新创建并赋值 - 卸载时:
el可能被置为null或保留(取决于是否启用 keep-alive)
映射关系服务于高效更新
Vue 需要快速定位“哪个 VNode 对应哪个真实 DOM”,否则每次 diff 后无法精准操作目标元素。通过在 VNode 上持有 el,就能在 patch 阶段直接操作对应的真实节点,跳过查找成本。
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
- 避免重复 querySelector:不用靠 class/id 去 DOM 树里找,VNode 自带引用
- 支持就地复用:相同 key 的 VNode 若
el存在,可直接复用其真实节点,仅更新 props/children - 便于递归处理:子 VNode 的
el是父节点el的后代,天然构成 DOM 树结构
注意 el 不等于 vnode.data.domProps
容易混淆的一点是:el 是真实 DOM 实例,而 vnode.data.domProps(Vue 2)或 vnode.props(Vue 3)只是待应用的属性描述对象。前者是结果,后者是指令。Vue 在 patch 时读取后者,再调用原生 API(如 el.textContent = xxx)作用于前者。
-
vnode.el→ 已存在的真实节点(如<div></div>) -
vnode.props→ 要设置的属性集合(如{ id: 'app', class: 'active' }) - 映射完成后,Vue 才会遍历
props,逐项同步到el上
响应式场景下 el 映射更需谨慎
当 VNode 由函数式组件或 h() 动态生成(例如 ElMessageBox 的 message 选项),如果提前固化 VNode,el 就无法跟随响应式数据变化而更新——因为那个 VNode 的 el 只会在首次 patch 时被赋值,之后不再重建。
- 错误写法:先
const v = h(...),再传入弹窗 →el锁死,双向绑定失效 - 正确写法:传入一个返回 VNode 的函数 → 每次渲染都生成新 VNode,触发新
el绑定和响应式追踪 - 本质是:映射关系必须与响应式依赖对齐,不能脱离 reactivity lifecycle









