汪图南
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • 介绍

    • 介绍和参考
  • 源码目录设计和架构设计

    • 设计
  • Rollup构建版本

    • Rollup基础知识
    • Vue中的Rollup构建
  • 从入口到构造函数整体流程

    • 整体流程
    • initGlobalAPI流程
    • initMixin流程
    • stateMixin流程
    • eventsMixin流程
    • lifecycleMixin流程
    • renderMixin流程
  • 响应式原理

    • 介绍
    • 前置核心概念
    • props处理
    • methods处理
    • data处理
    • computed处理
    • watch处理
    • 深入响应式原理
    • 依赖收集
    • 派发更新
    • nextTick实现原理
    • 变化侦测注意事项
    • 变化侦测API实现
  • 虚拟DOM和VNode

    • 虚拟DOM
    • VNode介绍
    • Diff算法
  • 组件化

    • 介绍
    • $mount方法
    • render和renderProxy
    • createElement
    • createComponent
    • 合并策略
    • update和patch
    • 组件生命周期
    • 组件注册
  • 编译原理

    • 介绍
    • compileToFunctions
    • parse模板解析
    • optimize优化
    • codegen代码生成
  • 扩展

    • 扩展
    • directive指令
    • filter过滤器
    • event事件处理
    • v-model
    • 插槽
    • Keep-Alive
    • Transition
    • Transition-Group
    • Vue.use插件机制
  • Vue-Router

    • 介绍
    • 路由安装
    • matcher介绍
    • 路由切换
    • 内置组件
    • 路由hooks钩子函数
  • Vuex

    • 介绍
    • Vuex安装
    • Vuex初始化
    • Vuex辅助API
    • Store实例API

VNode介绍

在DOM元素中有不同类型的节点,例如:元素节点、文本节点和注释节点,如果VNode实例代表节点的话,那么VNode实例应该能创建不同类型的实例,用来表示不同类型的节点,而VNode也确实可以做到。VNode类定义在src/core/vdom/vnode.js文件中,其代码如下:

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  devtoolsMeta: ?Object; // used to store functional render context for devtools
  fnScopeId: ?string; // functional scope id support

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>, 
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child (): Component | void {
    return this.componentInstance
  }
}

我们可以从以上代码看到,VNode有很多个属性,我们千万不要被这些属性吓到,最重要的属性只有几个:tag、data、children和key。其余很多属性只是在Vue中为适用不同的场景,额外添加的。

Vue中的VNode实例有几种类型,具体如下:

  • 注释节点:注释节点可以使用text和isComment两个属性来配合表示。
<template>
  <!-- 一个注释节点 -->
</template>

在Vue中,我们通过createEmptyVNode方法来定义注释节点,此方法跟VNode类是定义在同一个地方,其代码如下:

export const createEmptyVNode = (text: string = '') => {
  const node = new VNode()
  node.text = text
  node.isComment = true
  return node
}
  • 文本节点:文本节点比注释节点更简单,只需要text属性即可。
<template>
  一个文本节点
</template>

创建文本节点可以使用createTextVNode方法,此方法跟VNode类是定义在同一个地方,其代码如下:

export function createTextVNode (val: string | number) {
  return new VNode(undefined, undefined, undefined, String(val))
}
  • 克隆节点:克隆节点是将现有某个节点的所有属性全部克隆到另外一个新节点,让新节点和某个节点属性保持一致,唯一的区别是新克隆节点的isCloned属性为true,我们会在后续组件章节介绍克隆节点的具体作用。
// 原始文本节点
const originNode = {
  text: '原始节点',
  isCloned: false
}

// 克隆文本节点
const cloneNode = {
  text: '原始节点',
  isCloned: true
}

克隆一个节点,可以使用cloneVNode方法,它与VNode类是定义在同一个地方,其代码如下:

export function cloneVNode (vnode: VNode): VNode {
  const cloned = new VNode(
    vnode.tag,
    vnode.data,
    // #7975
    // clone children array to avoid mutating original in case of cloning
    // a child.
    vnode.children && vnode.children.slice(),
    vnode.text,
    vnode.elm,
    vnode.context,
    vnode.componentOptions,
    vnode.asyncFactory
  )
  cloned.ns = vnode.ns
  cloned.isStatic = vnode.isStatic
  cloned.key = vnode.key
  cloned.isComment = vnode.isComment
  cloned.fnContext = vnode.fnContext
  cloned.fnOptions = vnode.fnOptions
  cloned.fnScopeId = vnode.fnScopeId
  cloned.asyncMeta = vnode.asyncMeta
  cloned.isCloned = true
  return cloned
}
  • 元素节点:元素节点是我们日常开发中接触最多的,它可以使用tag、data、children和context几个属性配合表示。
<template>
  <div id="app" class="app-main">元素节点</div>
</template>

假设以上template模板,那么div元素节点可以使用VNode表示为:

const vnode = {
  tag: 'div',
  data: {
    attrs: {
      id: 'app'
    }
    class: 'app-main'
  },
  children: [VNode],
  context: vm
}
  1. tag表示为元素标签的类型,例如:p、div或者ul等。
  2. data表示节点上的数据,包括atts、style和class等。
  3. children表示子节点列表,它是一个VNode实例数组。
  4. context当前节点所处的编译作用域。
  • 组件节点:组件节点和元素节点有很多相似的地方,但它有两个独有的属性,分别是componentOptions和componentInstance,其中componentOptions表示组件的options选项,componentInstance表示当前组件的实例。
<template>
  <child-component />
</template>
最后更新时间: 2025/5/6 15:36
贡献者: wangtunan
Prev
虚拟DOM
Next
Diff算法