Vue3 :模板瘦身教练

115 阅读3分钟

一、编译阶段的优化

1. 静态提升(Static Hoisting)
  • 机制:将模板中不变的静态节点(如纯文本、无绑定的元素)提取为常量,在渲染函数外生成一次,后续复用。 示例
const _hoisted_1 = createVNode('p', null, '静态文本'); // 编译阶段提升
function render() {
  return [_hoisted_1, createVNode('div', null, this.dynamicData)];
}
  • 优势

    • 避免每次渲染时重复创建静态节点,减少内存消耗和虚拟 DOM 生成时间
    • 结合 预字符串化(Pre-stringification) ,将大量静态内容直接编译为字符串,进一步优化运行时性能
2. 动态标记(Patch Flag)
  • 机制:为动态节点 (如绑定变量、事件) 添加标记(如 `1` 表示文本变化,`8` 表示属性变化),指导运行时仅对比标记部分。 示例
createVNode('p', { class: dynamicClass }, '文本', 2 /* CLASS 动态标记 */);
  • 优势

    • 精准识别动态变化点,跳过无变化的静态内容,减少 Diff 范围,性能提升 30%~50%
3. 分块树(Block Tree)
  • 机制:将模板划分为动态块(Block)和静态块,每个块包含关联的动态子树,更新时仅处理动态块。 示例
function render() {
  return createBlock('div', [dynamicBlock, _hoisted_1]); // 仅 dynamicBlock 参与 Diff
}
  • 优势

    • 减少 Diff 深度和范围,尤其对大型列表和复杂组件效果显著
4. 事件监听缓存
  • 机制:若事件处理函数无依赖动态数据,则将其缓存为常量,避免每次渲染重新创建。 示例
const cachedHandler = () => console.log('click');
function render() {
  return createVNode('button', { onClick: cachedHandler });
}
  • 优势

    • 减少函数创建和内存占用,优化高频交互场景(如列表点击)
5. 指令优化(v-once 与 v-memo)
  • v-once:标记只渲染一次的静态内容,后续跳过 Diff。

  • v-memo:缓存动态节点的渲染结果,依赖项不变时直接复用。

  • 优势

    • 避免不必要的重复计算,适用于表格、大列表等场景
6. SSR 优化
  • 预字符串化:将静态内容直接编译为 HTML 字符串,减少服务端渲染时的虚拟 DOM 生成开销

二、渲染阶段的优化

1. 快速 Diff 算法(Fast Diff)
  • 机制

    • 预处理新旧子节点,通过 双端对比最长递增子序列 算法减少 DOM 操作。
  • 示例: 新旧子节点序列 `[A,B,C,D]``[A,C,B,D]`,仅移动 `C``B`,而非全量更新。

  • 优势

    • 减少 DOM 移动次数,性能提升 2~3 倍
2. Fragment 支持
  • 机制:允许组件有多个根节点,无需包裹额外元素。 示例
<template>
  <header>...</header>
  <main>...</main>
</template>
  • 优势

    • 减少冗余 DOM 层级,提升渲染效率(Vue 2 需强制单根节点)
3. 响应式系统升级
  • Proxy 代理

    • 替代 Vue 2 的 `Object.defineProperty`,支持动态属性增删和数组索引修改。

    • 优势

      • 无需 `Vue.set`/`Vue.delete`,简化开发
      • 按需触发更新,避免全量递归监听,内存占用降低 40%
4. 异步渲染与调度
  • vue2
  • setTimeout
  • 无优先级
  • 可能无限循环
  • 延迟高
  • vue3

  • Promise.then() -> 降级到 mutationObserver -> setTimeout

  • 按id和pre排序

  • 通过 allowRecurse & flushIndex 控制不无限递归

  • 低延迟

为什么宏任务不能实现批处理

  • 一个宏任务执行完毕后会清空微任务队列
  • 执行顺序可能由于浏览器不同而不稳定
  • 不同的宏任务是隔离上下文的 (而微任务是共享上下文的 )
  • 机制

    • 将多次数据变更合并为单次渲染任务,通过微任务队列调度更新。
    • 使用Promise.then() 包装成微任务执行,确保异步批量更新
    • 通过任务 id 排序优先级
  • 优势

    • 避免频繁重绘,优化高频操作(如实时输入、滚动)的流畅度

笔者才疏学浅,各位读者多多担待,不吝赐教。部分插图来自网络,侵删。