patchflag 是 vue 3 编译器在生成 vnode 时写入的位掩码整数标记,用于运行时精准识别需更新的动态部分;如 1 表示仅文本变化、2 表示仅 class 变化、3(1|2)表示文本与 class 同时变化,-1 和 -2 则分别代表静态提升与放弃优化。

Patch Flag 是 Vue 3 编译器在生成 VNode 时自动写入的整数标记,它不是运行时推断的,而是编译阶段就确定的“更新说明书”。每个数值对应一种或多种动态更新类型,本质是位掩码(bitmask),靠二进制位组合判断——比如 3 就是 1 | 2,表示同时有文本和 class 变化。
常见数值及其核心含义
你不需要记住全部,但以下这几个最常出现在编译输出里,直接决定运行时 patch 的行为:
-
1(TEXT):仅子节点文本内容会变,比如
{{ msg }}。运行时只执行el.textContent = next.children,连 props 和 class 都不碰。 -
2(CLASS):只有 class 绑定是响应式的,例如
:class="['btn', isActive ? 'active' : '']"。patch 时只调用patchClass,跳过 style、attrs、子节点比对。 -
4(STYLE):仅 style 对象绑定变化,如
:style="{ color: textColor }"。运行时只更新内联样式,不触发 class 或事件重绑。 -
8(PROPS):除 class/style 外的普通属性变动,比如
:id="dynamicId"、:data-role="role"。注意:它不包含事件监听器(事件归 HYDRATE_EVENTS 管理)。 - -1(HOISTED):整个节点完全静态,被提前提取到 render 函数外缓存。运行时根本不会重建,也不参与 diff,性能开销趋近于零。
- -2(BAIL):编译器无法精确分析动态性(如含复杂三元嵌套、函数调用等),放弃优化,走全量 diff。虽少见,但一旦出现,说明模板写法削弱了编译器能力。
数值怎么组合?看懂位运算逻辑
多个动态部分共存时,Patch Flag 不是相加,而是按位或(|)。因为每个标志对应一个独立的二进制位:
- TEXT = 1 → 二进制
0001 - CLASS = 2 → 二进制
0010 - STYLE = 4 → 二进制
0100 - TEXT | CLASS = 3 →
0001 | 0010 = 0011 - CLASS | STYLE = 6 →
0010 | 0100 = 0110
运行时用按位与(&)快速判断是否含某类更新:vnode.patchFlag & PatchFlags.CLASS 为真,才执行 class 更新逻辑。
为什么 -1 和 -2 是负数?
负数是 Vue 内部预留的特殊语义空间,避开正数位掩码区域,避免冲突:
- -1(HOISTED) 表示“完全跳过”,不是“某种更新”,而是“无需更新”。它不参与任何 patch 分支,直接复用节点。
- -2(BAIL) 表示“放弃优化策略”,相当于告诉 runtime:“别省事了,老老实实全量比对吧”。它常因模板中混用非响应式表达式或副作用函数触发。
怎么看实际数值?动手验证最准
别靠猜,直接看编译结果:
- 打开 Vue Devtools → 切换到 “Compiler Output” 面板,找组件的
render()函数,搜索createVNode; - 第三个或第四个参数就是 Patch Flag,比如
createVNode("div", props, children, 6)中的6; - 后面跟着的注释(如
/* CLASS | STYLE */)是编译器自动生成的,专为人眼可读; - 开发环境下也可打印:
console.log(instance.$.subTree.patchFlag),查看根 VNode 标记值。










