一句话总结
Vue3 把大量运行时的 diff 开销,提前到编译阶段做完了。
让运行时不再"瞎 diff",而是 靶向更新、只动动态的、不动静态的**,
从根源降低运行时成本。**
核心四大编译优化
1. PatchFlag 靶向更新(最重要)
编译时,编译器会分析模板,给动态节点打上补丁标记。
- 文本动态 → TEXT
- 类动态 → CLASS
- 样式动态 → STYLE
- 属性动态 → PROPS
- 多个动态 → 组合标记
运行时 diff 时:
只对比标记对应的内容,其他一律不看。
意义:
从全量 diff → 精准 diff
速度提升极大。
2. hoistStatic 静态提升
把纯静态节点 提升到 render 函数外部,
只创建一次 VNode,复用终身。
html
<div>
<span>静态文字</span> <!-- 提升 -->
<p>{{ msg }}</p> <!-- 不提升 -->
</div>
意义:
- 避免每次渲染重复创建静态 VNode
- 减少内存开销 + 减少 GC
3. cacheHandler 事件缓存
内联事件函数会被缓存,避免每次渲染创建新函数:
html
<button @click="() => count++">
编译后会缓存该函数,每次复用同一个引用。
意义:
- 避免不必要的组件更新
- 避免 props 浅比较失效
4. 静态树提升(Static Tree Hoisting)
一整棵子树全是静态 → 整棵提升,完全脱离 diff 流程 。
渲染时直接克隆 DOM,不走 VNode 对比。
意义:
大块静态内容几乎零运行时开销。
扩展优化(面试官常追问)
5. Block Tree 块优化
把动态节点收集到一个 flat 数组里,
更新时只遍历这个数组,不遍历整棵树。
真正做到:
树结构编写,数组结构更新
6. v-once 缓存整个节点
渲染一次后永久缓存,彻底不参与更新。
用于大量静态、只渲染一次的内容。
为什么 Vue3 必须做编译优化?
因为:
- Vue 是同步更新,不能靠 Fiber 切片扛压力
- 必须从源头减少更新量、减少 diff、减少 DOM 操作
- 让运行时足够轻、足够快,保证不阻塞主线程
一句话:
Vue3 = 精准响应式 + 极致编译优化 = 运行时极快
面试满分口述版(直接背)
Vue3 的编译优化核心是把运行时压力前置到编译阶段:
- PatchFlag 标记动态节点,实现靶向更新,只 diff 变化内容。
- hoistStatic 静态提升,避免重复创建静态 VNode。
- cacheHandler 缓存内联事件,避免不必要更新。
- Block Tree 把动态节点打平,更新时只遍历动态集合。
- 再配合最长递增子序列减少 DOM 移动,让运行时达到理论最优速度。