patchflags.stable_fragment表示子节点顺序稳定的fragment,用于跳过移动逻辑、仅局部更新;生成场景包括:1.组件有多个根节点;2.v-if分支下多个无key同级子节点;3.插槽出口渲染顺序固定的子节点;4.表达式为常量且未绑定key。

PatchFlags.STABLE_FRAGMENT 表示一个子节点顺序稳定、无需重排的 Fragment,它的核心作用是告诉 Vue:这个片段里的子节点在更新时不会改变相对位置,因此 diff 过程可以跳过“移动节点”逻辑,只做属性或内容的局部更新,大幅提升性能。
什么情况下会生成 STABLE_FRAGMENT
编译器会在以下几种典型场景中自动设置该 flag:
- 组件有多个根节点(即没有包裹元素的模板),例如:
<div></div> <span></span> - v-if 分支下存在多个同级子节点,且这些节点本身没有 key,也没有动态顺序变化风险
- 插槽出口(
<slot></slot>)渲染出的多个静态或顺序固定的子节点 -
<template v-for></template>的表达式是常量(如v-for="item in ['a','b','c']"),且未绑定 key —— 此时 Vue 判定其结构稳定
为什么它能提升 diff 效率
普通 Fragment 在 diff 时需执行完整双端比对(首首、尾尾、首尾、尾首),而 STABLE_FRAGMENT 直接跳过节点移动判断。Vue 只需逐个比对对应位置的新旧子节点,若类型相同就复用 DOM,否则替换。这省去了查找插入位置、计算移动索引、调整 ref 等开销。
举个例子:一个无 key 的 v-if 块渲染了 [<h1>, </h1>
<p>, <button>]</button></p>,后续数据变化仅影响 <p></p> 的文本内容 —— Vue 就只 patch 第二项,其余两个节点完全跳过比较。
和 KEYED_FRAGMENT / UNKEYED_FRAGMENT 的区别
三者都属于 Fragment 类型,但语义和处理策略不同:
- STABLE_FRAGMENT:强调“顺序绝对不变”,哪怕没 key,也禁止移动节点
- KEYED_FRAGMENT:子节点带 key,允许重排,diff 依赖 key 映射做精准复用
- UNKEYED_FRAGMENT:子节点无 key,且顺序可能变动,必须走完整双端 diff 流程
注意:STABLE_FRAGMENT 不等于“不能有 key”,而是“即使没 key,也能保证顺序稳定”。一旦子节点出现 key 或动态插入/删除逻辑,编译器就会降级为 KEYED 或 UNKEYED。
开发者需要注意的边界情况
虽然编译器大多能自动识别,但某些写法会意外破坏稳定性:
- 在多根模板中混用
v-if和v-else且分支返回不同数量节点 - 插槽内容包含条件渲染或循环,且父组件未提供
$stablehint - 使用
ref、自定义指令或v-once的节点被包裹在多根结构中 —— 这些节点会被视为不稳定,导致整个 Fragment 无法标记为 STABLE
遇到性能瓶颈时,可检查生成的 render 函数中 vnode 的 patchFlag & PatchFlags.STABLE_FRAGMENT 是否为真,确认编译结果是否符合预期。










