ShapeFlags 是 Vue 3 中用位掩码标识 VNode 类型与特征的整数,通过位运算(如 | 和 &)高效组合与判断节点类型(如 ELEMENT、STATEFUL_COMPONENT),兼顾性能、内存与扩展性。

Vue 3 的 VNode 通过 shapeFlags 字段用位掩码(bitmask)高效标识节点类型和特征,避免冗余字段和条件判断。理解其位运算逻辑,是读懂源码、调试渲染流程、自定义渲染器的关键。
ShapeFlags 是什么?为什么用位运算?
shapeFlags 是一个整数,每个二进制位代表一种节点“形状”(如是否为元素、组件、文本、Fragment 等)。Vue 定义了一系列常量(如 ELEMENT = 1 << 0、STATEFUL_COMPONENT = 1 << 2),通过左移生成互不重叠的标志位。多个类型可同时存在(比如一个有状态组件也可能是 Suspense 的子节点),用按位或(|)组合;判断是否包含某类型则用按位与(&)。
好处是:内存占用小、判断极快(单次位运算)、支持类型叠加、便于枚举和扩展。
核心 ShapeFlags 常量及含义
以下为 Vue 3.4 源码中关键标识(路径:packages/shared/src/shapeFlags.ts):
立即学习“前端免费学习笔记(深入)”;
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
-
ELEMENT = 1(二进制
0001):原生 DOM 元素,如<div>、<span> -
STATEFUL_COMPONENT = 1 << 1 = 2(
0010):带 setup / data / methods 的 Options API 或 Composition API 组件 -
FUNCTIONAL_COMPONENT = 1 << 2 = 4(
0100):纯函数式组件(无状态、无实例) -
TEXT_CHILDREN = 1 << 3 = 8(
1000):children是字符串(非数组或 null) -
ARRAY_CHILDREN = 1 << 4 = 16:
children是 VNode 数组 - SUSPENSE = 1 << 9 = 512:Suspense 节点
- TELEPORT = 1 << 10 = 1024:Teleport 节点
-
FRAGMENT = 1 << 11 = 2048:Fragment 节点(
<template>)
注意:一个组件节点的 shapeFlags 可能同时含 STATEFUL_COMPONENT | TEXT_CHILDREN(如返回字符串的 setup 组件),或 TELEPORT | ARRAY_CHILDREN(Teleport 包含多个子节点)。
常见位运算操作与实战判断
源码中高频出现以下模式,直接对应实际需求:
- 判断是否为组件:
vnode.shapeFlags & COMPONENT(COMPONENT = STATEFUL_COMPONENT | FUNCTIONAL_COMPONENT) - 判断是否为普通元素:
vnode.shapeFlags & ELEMENT - 判断 children 类型:
if (vnode.shapeFlags & TEXT_CHILDREN) { ... }或if (vnode.shapeFlags & ARRAY_CHILDREN) { ... } - 判断是否为高阶容器:
if (vnode.shapeFlags & (TELEPORT | SUSPENSE | FRAGMENT)) { ... } - 创建复合节点:
createVNode(Component, null, 'hello', shapeFlags: STATEFUL_COMPONENT | TEXT_CHILDREN)
这种写法比用字符串类型字段(如 type: 'component')或对象属性(isComponent: true)更轻量,且编译时可静态分析优化。
与 PatchFlags 的区别与协作
别混淆 shapeFlags 和 patchFlags:
- shapeFlags 描述“**这是什么类型的节点**”(宏观结构);
- patchFlags 描述“**这个节点哪些部分需要更新**”(微观 diff 策略,如 TEXT、PROPS、HYDRATE_EVENTS)。
二者正交:一个 shapeFlags = ELEMENT 的 <div> 节点,其 patchFlags 可能是 0(全量 diff)或 2(仅需 patch props)。
渲染器先看 shapeFlags 分支进入不同处理逻辑(如 processElement vs processComponent),再根据 patchFlags 在内部跳过无需更新的子流程。

















