Vue Composition API

66 阅读3分钟

Vue 3 的 Composition API 是一套函数式 API,用于在组件中更灵活地组织逻辑代码。它解决了 Options API 在大型组件中逻辑分散的问题,提供更好的代码组织和复用能力。

核心 API 概览:

  1. 响应式 API

    • ref():创建基本类型响应式数据
    • reactive():创建对象类型响应式数据
    • computed():创建计算属性
    • watch() / watchEffect():监听响应式变化
  2. 生命周期钩子

    • onMounted() / onUnmounted() 等替代 mounted/unmounted
  3. 依赖注入

    • provide() / inject()
  4. 工具函数

    • toRefs():保持响应式解构
    • shallowRef() / shallowReactive():浅层响应式

完整示例代码 (.vue 文件)

<script setup>
import {
  ref, reactive, computed, watch, watchEffect,
  onMounted, onUnmounted, provide, inject,
  toRefs, shallowRef, triggerRef, customRef
} from 'vue'

// 1. ref - 基本类型响应式
const counter = ref(0)
const increment = () => counter.value++

// 2. reactive - 对象响应式
const user = reactive({
  firstName: 'John',
  lastName: 'Doe',
  age: 30
})

// 3. computed - 计算属性
const fullName = computed(() => `${user.firstName} ${user.lastName}`)

// 4. watch - 侦听特定值
watch(counter, (newVal, oldVal) => {
  console.log(`计数器变化: ${oldVal} → ${newVal}`)
})

// 5. watchEffect - 自动追踪依赖
watchEffect(() => {
  console.log(`watchEffect: ${fullName.value} 年龄 ${user.age}`)
})

// 6. 生命周期钩子
onMounted(() => console.log('组件已挂载'))
onUnmounted(() => console.log('组件将卸载'))

// 7. provide/inject - 依赖注入
const appTheme = ref('dark')
provide('theme', appTheme)

// 8. toRefs - 解构保持响应性
const { firstName, lastName } = toRefs(user)

// 9. shallowRef - 浅层响应式
const shallowObj = shallowRef({ deep: { value: 100 } })
const updateShallow = () => {
  shallowObj.value.deep.value += 10
  triggerRef(shallowObj) // 手动触发更新
}

// 10. customRef - 自定义ref
function useDebouncedRef(value, delay = 500) {
  let timeout
  return customRef((track, trigger) => ({
    get() {
      track()
      return value
    },
    set(newValue) {
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        value = newValue
        trigger()
      }, delay)
    }
  }))
}
const searchText = useDebouncedRef('')

// 11. 组件逻辑复用示例
function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  const double = computed(() => count.value * 2)
  
  function reset() {
    count.value = initialValue
  }
  
  return { count, double, reset }
}

const { count: otherCount, double: otherDouble } = useCounter(10)

// 12. 从inject获取值 (在子组件中演示)
const childTheme = inject('theme', 'light')
</script>

<template>
  <div>
    <!-- 基础响应式 -->
    <div class="section">
      <h2>响应式基础</h2>
      <button @click="increment">计数: {{ counter }}</button>
      <input v-model="user.firstName" placeholder="名字">
      <input v-model="user.lastName" placeholder="姓氏">
      <p>全名: {{ fullName }}</p>
    </div>

    <!-- 解构响应式 -->
    <div class="section">
      <h2>toRefs 解构</h2>
      <input v-model="firstName" placeholder="名字 (解构)">
      <input v-model="lastName" placeholder="姓氏 (解构)">
    </div>

    <!-- 浅层响应式 -->
    <div class="section">
      <h2>浅层响应式</h2>
      <button @click="updateShallow">更新深层值: {{ shallowObj.deep.value }}</button>
      <p>需要手动 triggerRef 更新视图</p>
    </div>

    <!-- 自定义Ref -->
    <div class="section">
      <h2>自定义Ref (防抖)</h2>
      <input v-model="searchText" placeholder="输入会500ms防抖">
      <p>实时值: {{ searchText }}</p>
    </div>

    <!-- 逻辑复用 -->
    <div class="section">
      <h2>逻辑复用</h2>
      <p>计数器: {{ otherCount }} × 2 = {{ otherDouble }}</p>
      <button @click="otherCount++">增加</button>
    </div>

    <!-- 依赖注入 -->
    <div class="section">
      <h2>依赖注入</h2>
      <p>当前主题: {{ childTheme }}
        <button @click="appTheme = appTheme === 'dark' ? 'light' : 'dark'">
          切换主题
        </button>
      </p>
    </div>
  </div>
</template>

<style scoped>
.section {
  margin: 20px 0;
  padding: 15px;
  border: 1px solid #eee;
  border-radius: 8px;
}
button {
  margin: 0 5px;
  padding: 5px 10px;
}
input {
  margin: 5px;
  padding: 5px;
}
</style>

核心 API 使用说明:

  1. 响应式基础

    • ref(): 用于基本类型,通过 .value 访问
    • reactive(): 用于对象类型,直接访问属性
    • 组合式优于选项式:相关逻辑可以组织在一起
  2. 计算和侦听

    • computed(): 基于依赖缓存计算结果
    • watch(): 精确控制侦听源和回调时机
    • watchEffect(): 自动追踪依赖,立即执行
  3. 生命周期

    • 前缀 on + 生命周期名 (onMounted, onUpdated 等)
    • 可在同一位置组织相关逻辑
  4. 依赖注入

    • provide() 在祖先组件提供值
    • inject() 在后代组件获取值
    • 避免 props 逐级传递
  5. 高级响应式

    • toRefs(): 解构 reactive 对象保持响应性
    • shallowRef(): 只跟踪 .value 变化
    • customRef(): 创建自定义 ref 实现特殊逻辑
  6. 代码复用

    • 封装自定义 Hook (如示例中的 useCounter)
    • 类似 React Hooks 的逻辑复用能力

最佳实践:

  1. 使用 <script setup> 简化语法
  2. 复杂逻辑拆分为独立组合函数
  3. 相关功能组织在同一代码区域
  4. 使用 TypeScript 增强类型安全
  5. 大型项目优先选择 Composition API

Composition API 特别适合复杂组件和逻辑复用场景,通过函数组合的方式使代码更清晰、更易维护,同时保持完整的响应式能力。