Skip to content

VNode — 虚拟节点 ​

为什么需要虚拟 DOM ​

直接操作真实 DOM 成本高昂。虚拟 DOM 是一层 JavaScript 对象抽象,通过对比新旧 VNode 差异,批量高效地更新真实 DOM。

VNode 类 ​

js
// src/core/vdom/vnode.js

export default class VNode {
  constructor(
    tag,        // 标签名("div")或组件构造函数
    data,       // attrs、props、on、style、class 等
    children,   // 子节点数组
    text,       // 文本内容
    elm,        // 对应的真实 DOM 节点
    context,    // 组件实例
    componentOptions, // 组件选项
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined       // SVG namespace
    this.context = context
    this.key = data && data.key  // 用于 diff 优化

    // 组件相关
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined

    // 其他
    this.raw = false          // 是否纯文本
    this.isStatic = false     // 是否静态节点(编译优化)
    this.isRootInsert = true  // 是否根插入(v-for/v-if)
    this.isComment = false    // 是否注释节点
    this.isCloned = false     // 是否克隆节点
    this.isOnce = false       // 是否 v-once
  }
}

VNode 的几种类型 ​

js
// 1. 文本节点
{ text: "hello", children: undefined }

// 2. 元素节点
{ tag: "div", children: [...], text: undefined }

// 3. 注释节点
{ text: "", isComment: true, children: undefined }

// 4. 组件节点
{
  tag: "vue-component-1-my-component",
  componentOptions: { Ctor, propsData, listeners, tag, children },
  children: undefined
}

// 5. 克隆节点(优化静态节点重复使用)
{ isCloned: true, ...克隆自原 VNode }

创建 VNode 辅助函数 ​

js
// src/core/vdom/vnode.js

// 创建元素 VNode
export function createElementVNode(tag, data, children) {
  return new VNode(tag, data, children, undefined, undefined)
}

// 创建文本 VNode
export function createTextVNode(val) {
  return new VNode(undefined, undefined, undefined, String(val))
}

// 创建空 VNode
export const createEmptyVNode = () => {
  const node = new VNode()
  node.text = ""
  node.isComment = true
  return node
}

编译后 render 中的 VNode 调用 ​

js
// template: <div><p>hello</p></div>
// 编译后:
function render() {
  return _c("div", [
    _c("p", [_v("hello")]),
  ])
}

// _c  = createElementVNode(元素)
// _v  = createTextVNode(文本)

基于 Vue 2.7 & Vue 3.4 源码