Vue3 编译优化
一、Vue 3 编译优化的核心背景Vue 2 的虚拟 DOM 存在一个核心问题每次数据更新时会对整个虚拟 DOM 树进行全量对比diff哪怕大部分节点是静态的不会变也会被遍历对比造成性能浪费。Vue 3 的编译优化核心思路是在编译阶段提前分析模板给节点打标记、做预处理让运行时的 diff 只对比「真正需要更新的节点」从「全量 diff」变成「定向 diff」大幅提升性能。二、核心编译优化点详解静态提升Static Hoisting概念把模板中不会随数据变化的静态节点 / 属性比如纯文本、固定样式、无绑定的标签从渲染函数中「提升」出去只在初始化时创建一次后续更新时复用避免每次渲染都重新创建虚拟 DOM 节点。对比示例Vue 2 编译结果伪代码// 每次渲染都会重新创建 divHello/div 这个静态节点functionrender(){returncreateVNode(div,null,[createVNode(div,null,Hello),// 静态节点每次都重建createVNode(span,null,state.msg)// 动态节点])}Vue 3 编译结果伪代码// 静态节点被提升到渲染函数外部只创建一次consthoistedcreateVNode(div,null,Hello)functionrender(){returncreateVNode(div,null,[hoisted,// 复用已创建的静态节点createVNode(span,null,state.msg)])}实际模板示例template !-- 静态节点无任何动态绑定会被提升 -- div classtitleVue 3 编译优化/div !-- 动态节点依赖 msg 数据 -- div{{ msg }}/div /templateVue 3 编译时会把div classtitleVue 3 编译优化/div提升到渲染函数外只有div{{ msg }}/div会在每次更新时重新处理。树结构打平Tree Flattening概念Vue 3 会把嵌套的虚拟 DOM 树「打平」成一个数组结构避免深度递归遍历。简单说把多层嵌套的节点转换成一维数组记录每个节点的父子关系diff 时只需遍历一维数组无需递归降低时间复杂度。对比理解Vue 2 虚拟 DOM 结构嵌套树// 嵌套结构diff 时要递归遍历constvnode{tag:div,children:[{tag:ul,children:[{tag:li,children:[{tag:span,children:1}]},{tag:li,children:[{tag:span,children:2}]}]}]}Vue 3 虚拟 DOM 结构打平数组// 打平成数组记录 parent/children 索引无需递归constvnodes[{tag:div,children:[1]},// 索引 0div子节点是索引 1{tag:ul,children:[2,3]},// 索引 1ul子节点是 2、3{tag:li,children:[4]},// 索引 2li子节点是 4{tag:li,children:[5]},// 索引 3li子节点是 5{tag:span,children:1},// 索引 4span{tag:span,children:2}// 索引 5span]diff 时只需遍历这个一维数组通过索引快速找到父子节点效率远高于递归嵌套树。PatchFlags补丁标记概念Vue 3 在编译阶段给动态节点打上「补丁标记」标记该节点的「动态类型」比如仅文本更新、仅 class 更新、仅属性更新等。运行时 diff 时只根据标记检查对应类型的变化无需全量对比节点的所有属性。核心 PatchFlags 枚举常用| 标记值 | 含义 | 场景示例 || ------------ | ------------- | ----------------|| 1 | TEXT |div{{ msg }}/div|| 2 | CLASS |div :classcls/div|| 4 | STYLE |div :stylesty/div|| 8 | PROPS |div :idid/div|| 64 | FULL_PROPS |含动态 key 的属性如 :[key]val|| 128 | HYDRATE_EVENTS |绑定了事件的节点|示例模板template div :classboxCls idbox{{ content }}/div /templateVue 3 编译后该节点会被打上 PatchFlags.TEXT | PatchFlags.CLASS即 1 2 3的标记。运行时更新时只会检查文本内容content是否变化class 属性boxCls是否变化完全忽略 id“box” 这个静态属性也不会检查其他无关属性精准更新。缓存优化Vue 3 对编译后的渲染函数、计算属性、事件处理等做了多层缓存核心包括缓存渲染函数编译后的渲染函数会被缓存只有模板变化时才重新编译避免重复解析模板。缓存事件处理函数对 click“handleClick” 这类无参数的事件绑定Vue 3 会缓存函数引用避免每次渲染都创建新函数解决 Vue 2 中 click“() handleClick(1)” 导致的不必要更新问题。缓存 vnode 创建对于结构固定的动态节点如 v-for 中固定结构的项复用 vnode 结构只更新动态数据。示例template !-- handleClick 会被缓存不会每次渲染创建新函数 -- button clickhandleClick点击/button !-- 带参数的箭头函数Vue 3 不会优化缓存每次编译生成新的箭头函数 -- button click() handleClick(1)点击传参/button /template三、Vue 3 性能提升原因总结 完整示例代码性能提升核心原因编译阶段预处理通过静态提升、PatchFlags 减少运行时无用计算虚拟 DOM 优化树结构打平降低 diff 复杂度从 O (n²) 趋近于 O (n)缓存策略复用静态节点、渲染函数、事件函数减少重复创建 / 计算按需编译只处理动态内容静态内容全程复用。总结Vue 3 编译优化的核心是「编译期预处理运行期精准更新」静态提升减少重复创建PatchFlags 精准标记动态类型树打平降低 diff 复杂度性能提升的关键从「全量 diff 虚拟 DOM」变成「只处理动态内容」静态内容全程复用