Skip to content

Vue3

Composition API

Vue3 新增了 Composition API,它是一组基于函数的 API,可以更灵活地组织和复用组件逻辑。解决了 Vue2 中选项式 API 带来的逻辑碎片化问题,尤其适合大型复杂项目。

在 Vue2 中,逻辑复用主要依赖 mixins、高阶组件和自定义指令,但这些方式存在命名冲突、数据来源不清晰等问题。而 Composition API 允许将组件逻辑提取为独立的函数(通常称为 "组合函数"),这些函数可以在多个组件间共享:

下面是一个简单的示例:

javascript
// useCounter.js - 一个可复用的计数器逻辑
import { ref, computed, onMounted } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  
  const doubleCount = computed(() => count.value * 2)
  
  // 生命周期钩子也可以包含在复用逻辑中
  onMounted(() => {
    console.log('Counter initialized')
  })
  
  return {
    count,
    increment,
    decrement,
    doubleCount
  }
}

在组件中使用这个组合函数:

js
import { useCounter } from './useCounter'

export default {
  setup() {
    const { count, increment, doubleCount } = useCounter(10)
    return { count, increment, doubleCount }
  }
}

非常类似于React Hook,但是Vue3 的 Composition API 允许在组件的 setup 函数中使用这些组合函数,能在条件或循环语句中调用,且可以更好地控制响应式数据的生命周期。

为什么Composition API可以在循环或条件语句中使用,而react hook不行?

1. React Hook 的调用规章限制

  • React Hook 的核心机制是通过调用顺序来识别和关联状态:
    • 每次组件渲染时,Hook 的调用顺序必须保持一致。
    • React 内部维护一个链表,按顺序存储每个 Hook 的状态。
  • 如果在循环或条件中调用 Hook,会破坏调用顺序的一致性:
    • 例如,条件不满足时跳过某个 Hook,会导致后续 Hook 的索引错位。
    • 这会使 React 错误地将状态与 Hook 关联,引发难以调试的 Bug(如状态值错乱

2. Vue Composition API 的灵活性

  • (1)基于显式的 setup() 初始化,Vue3 的组件逻辑通过 setup() 函数一次性初始化:
    • 组合函数(如 useFetch)在 setup() 执行时被调用,返回的响应式状态会被缓存。
    • 后续渲染时直接使用已创建的状态,无需重新调用组合函数。
  • (2)响应式系统自动追踪依赖,Vue 的响应式系统主动追踪依赖:
    • 当状态变化时,Vue 会自动找到使用该状态的 DOM 节点并更新。
    • 组合函数的调用位置不影响依赖关系,因为依赖是在运行时通过 getter/setter 追踪的。

虚拟 DOM 重写

Vue3 对虚拟 DOM 算法进行了彻底重写,主要优化点包括:

  1. Patch Flag(编译时标记)
    • 原理:在编译阶段分析模板,为动态节点添加标记(如 TEXT、CLASS、STYLE 等),只更新需要变化的部分。
    • 效果:减少了运行时的 Diff 计算量,提升渲染速度
vue
   <!-- 编译后会标记动态文本节点 -->
<div>
  <span>{{ message }}</span> <!-- 标记为 TEXT -->
  <span>静态文本</span>      <!-- 完全跳过 Diff -->
</div>
  1. 静态提升
    • 原理:将模板中不变的静态节点提升到渲染函数外部,避免每次渲染都重新创建。
    • 效果:减少了内存分配和垃圾回收,提高渲染性能。
vue
// 编译前
const render = () => <div>静态内容</div>;

// 编译后(静态内容被提升)
const staticNode = <div>静态内容</div>;
const render = () => staticNode;

Proxy 响应式

Vue3 使用 ES6 Proxy 替代 Vue2 的 Object.defineProperty,带来以下优势:

  1. 更高效的响应式追踪
    • 深度监听:Proxy 可以直接监听对象 / 数组的变化,无需像 Vue2 那样递归遍历所有属性。
    • 新增 / 删除属性支持:Vue2 无法检测对象属性的新增或删除,而 Proxy 可以。
js
const obj = reactive({});
obj.newProp = 123; // Vue3 可直接追踪新增属性
  1. 更少的内存开销
    • Proxy 是原生对象,相比 Vue2 的响应式包装对象,内存占用更小。

编译时优化

Vue3 的编译器(@vue/compiler-dom)引入了多项编译时优化:

  1. 区块(Block)系统
  • 原理:将模板划分为多个区块,每个区块内的动态节点形成一棵稳定的树结构,减少 Diff 范围。
  • 效果:提升复杂嵌套结构的更新效率。
vue
<!-- 编译后会将 v-if/v-for 等指令转换为区块 -->
<template>
  <div>
    <div v-if="condition">动态内容</div> <!-- 独立区块 -->
    <ul>
      <li v-for="item in list">{{ item }}</li> <!-- 列表区块 -->
    </ul>
  </div>
</template>
  1. 事件缓存
    • 原理:对静态事件处理函数(如 @click="handleClick")进行缓存,避免重复创建。
    • 效果:减少内存占用和渲染开销。

组件初始化优化

Vue3 重写了组件初始化流程,提升组件创建速度:

  • 更轻量的组件实例 :Vue3 的组件实例体积比 Vue2 减少约 40%,内存占用更低。
  • 更快的创建速度 :通过优化组件选项合并和初始化逻辑,组件创建速度提升约 1.3~2 倍。

Tree-shaking 支持

  • Vue3 重构为基于 ES 模块的设计,支持 Tree-shaking(按需打包):
  • Vue3 基础运行时体积约 11KB(gzipped),相比 Vue2 减少约 41%。

Typescript支持

Vue3 从底层架构开始就采用 TypeScript 重写,而 Vue2 是用 JavaScript 编写的,后期通过类型声明文件(.d.ts)提供有限支持。

自定义渲染器

Vue3 提供了 @vue/runtime-core,允许创建自定义渲染器(如渲染到 WebGL、原生应用等):

  • 更灵活的渲染目标: 可针对特定场景(如游戏、桌面应用)优化渲染性能。
  • 更低的渲染层抽象成本: 相比 Vue2,减少了中间层的性能损耗。

新的内置组件

1. Teleport 组件

允许将组件内容渲染到 DOM 中的其他位置,而不必受限于组件的嵌套层级。常用于创建模态框、弹窗等需要脱离父容器布局的场景。

应用场景 :模态框 / 对话框 , 悬浮提示(Tooltip) , 下拉菜单

2. Suspense 组件

用于处理异步组件的加载状态,提供优雅的加载中(fallback)体验。可与异步组件(defineAsyncComponent)和组合式 API 中的 async setup() 配合使用。

vue
<template>
  <Suspense>
    <!-- 异步组件 -->
    <template #default>
      <AsyncComponent />
    </template>
    
    <!-- 加载中状态 -->
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

<script>
import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
  import('./AsyncComponent.vue')
);

export default {
  components: {
    AsyncComponent
  }
};
</script>

多根结点 Fragment

Vue3 支持组件返回多个根节点(Fragment),不再需要包裹在单一根节点内。这使得组件结构更灵活,减少了不必要的 DOM 层级。

vue
<!-- Vue3 中合法 -->
<template>
  <header>页面头部</header>
  <main>主要内容</main>
  <footer>页面底部</footer>
</template>