PENGINE
算法
数据库
前端
后端
工具
vue3源码分析:vnode标记系统
2026-07-23 14:29:32

vue3 使用VNode(虚拟节点,树结构的js对象) 对真实 DOM 进行描述,更新页面时就是通过对比新旧 VNode 树,最小化操作实现dom的修改。 结构类似如下:

{
anchor:null,
appContext:
{app: {…}, config: {…}, mixins: Array(0), components: {…}, directives: {…}, …},
children: null,
component:{uid: 0, vnode: {…}, type: {…}, parent: null, appContext: {…}, …},
ctx: null,
dirs: null,
dynamicChildren: null,
dynamicProps: null,
el: text,
key: null,
patchFlag:0,
props: null,
ref: null,
scopeId: null,
shapeFlag: 4,
slotScopeIds: null,
ssContent: null,
ssFallback: null,
staticCount: 0,
suspense: null,
target:null,
targetAnchor: null,
targetStart: null,
transition: null,
type:{
  template: 
    '<div v-on:click="setAge">修改年龄</div><div>{{age}}</div><div>Hello Vue</div>',
  setup: ƒ, 
  render: ƒ
},
__v_isVNode: true,
__v_skip: true
}

vnode使用属性type描述了当前vnode节点的具体内容,使用属性shapeFlag描述内容类型,shapeFlag所有值如下:

export enum ShapeFlags {
  ELEMENT = 1, //普通 DOM 元素
  FUNCTIONAL_COMPONENT = 1 << 1,//函数式组件
  STATEFUL_COMPONENT = 1 << 2, //普通组件
  TEXT_CHILDREN = 1 << 3,//子元素文本 例如:<div>Hello</div>
  ARRAY_CHILDREN = 1 << 4, //子元素列表//例:<div><span/><span/></div>
  SLOTS_CHILDREN = 1 << 5,//组件插槽
  TELEPORT = 1 << 6,
  SUSPENSE = 1 << 7,//异步组件
  COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,//组件应该缓存
  COMPONENT_KEPT_ALIVE = 1 << 9,//已经被缓存过
  COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT,//所有组件类型
}

位运算符 << , | , & 含义:

"<<"左移运算符
a << b  表示把数字 a 的二进制位向左移动 b 位
例:1 << 2
数字1的二进制 是 00000001 ,左移 2 位 即为 00000100

"|" 按位或
00000001
   |
00000100
------------
00000101

"&" 按位与
00001001
  &
00000001
---------
00000001

vue为什么不用十进制表示呢?如下:

1 << 2 = 00000100(二进制) = 4(十进制)

因为vue中需要 组合shapeFlag,例如 ELEMENT |TEXT_CHILDREN 假设:

const ELEMENT = 1
const TEXT_CHILDREN = 2
const COMPONENT = 3

此时一个vnode 的shapeFlag 需要 ELEMENT |TEXT_CHILDREN 来描述: 那么组合标识ELEMENT+TEXT_CHILDREN = 3 此时就与COMPONENT =3冲突了,分不清 3 具体表示什么。

而使用二进制,每个 ShapeFlag 又占用唯一的 bit 位。因此不论单个ShapeFlag ,还是组合ShapeFlag, 最终的值 都是唯一,且知道具体由哪些ShapeFlag组成。

在patch过程中 需要大量对当前节点的判断,二进制运算效率高于js函数判断。

辽ICP备15018437号-3