v-for 内部节点无法静态提升,因编译器跳过所有含 v-for 的节点及其后代;即便内容固定,也因数量不确定而被视为动态,仅静态属性可被单独提取为常量 props。

编译器不会对 v-for 内部的节点做静态提升——哪怕内容看起来完全固定,只要包裹在 v-for 中,整个子树就失去被识别为静态的资格。
为什么 v-for 里的节点无法被提升
Vue 编译器在分析模板时,会明确跳过所有含 v-for 指令的节点及其后代。这不是保守判断,而是逻辑必然:v-for 表示“可能重复多次”,而静态提升的前提是“结构和数量绝对唯一且不变”。哪怕你写的是 v-for="item in [1]",编译器也无法在编译期确认数组长度恒为 1(它不执行运行时逻辑),因此整块内容都被视为动态。
-
v-for节点本身会被编译为 keyed block,带openBlock(true),用于高效 diff - 其内部任意节点,无论是否含插值、是否 class 是字面量,都不会进入 hoist 流程
- 源码中
hoistStatic.ts明确设置了doNotHoistNode标志,直接屏蔽提升路径
但静态属性仍可单独提取
虽然整个 v-for 项不能提升,编译器仍会对其中的静态属性做精细化分离:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 例如
<div v-for="item in list" :key="item.id" class="item" data-id="static">{{ item.name }}</div> - 其中
class="item"和data-id="static"会被提取为常量 props 对象(如_hoisted_1 = { class: "item", "data-id": "static" }) - 渲染时通过
_mergeProps({ key: item.id }, _hoisted_1)合并,避免每次构造新 props
真正可行的优化方式
想让循环中部分结构享受静态优化,得把“静态”从循环体内抽出来,交给编译器独立识别:
- 把纯静态内容移到
v-for外层,比如列表标题、分隔线、页脚等 - 用
<template v-for></template>包裹多元素,避免无意义的 DOM 容器干扰,但注意它本身仍是动态 block - 对不需要响应式更新的子组件,加
v-once——它作用于运行时,可跳过 patch,适合展示型卡片、图标等 - 若某段 HTML 完全不依赖循环变量,考虑改用普通
v-for外的静态节点 + 条件控制显隐,而非硬塞进循环
验证是否生效的小技巧
不用猜,直接看编译结果:
- 构建时开启
build.report,查看生成的 render 函数里是否有_hoisted_x变量 - 在 Vue DevTools 的 “Compiler” 面板中,观察 AST 节点是否标记
static: true - 检查输出代码:若某节点出现在
render函数体外、且调用_createVNode(..., -1),说明已成功提升









