Vue 3 组合式 API 完全指南:从入门到实战
Vue 3 组合式 API 完全指南,详解 ref、reactive、computed、watch 等核心 API,介绍 Composables 逻辑复用方式,以及 TypeScript 集成和性能优化技巧。
# Vue 3 组合式 API 完全指南:从入门到实战
## 前言
Vue 3 的发布带来了一个全新的编程范式——组合式 API(Composition API)。这不仅是 Vue 历史上最重要的更新之一,更是现代前端开发的一次重大变革。在本文中,我们将深入探讨组合式 API 的方方面面,帮助你从零基础到能够熟练运用这一强大工具。
## 什么是组合式 API?
组合式 API 是 Vue 3 引入的一种新的组件编写方式,它允许我们使用函数式的 API 来组织组件逻辑。与传统的选项式 API(Options API)相比,组合式 API 提供了更好的逻辑复用能力、更灵活的代码组织方式,以及更清晰的类型推导支持。
### 为什么需要组合式 API?
在大型项目中,我们经常会遇到一些问题:
1. **逻辑复用困难**:当多个组件需要使用相同的逻辑时,我们只能通过 mixins 来实现,但这会导致数据来源不清晰、命名冲突等问题。
2. **代码组织混乱**:随着组件功能增加,相关逻辑被迫分散在不同的选项中(data、methods、computed、watch 等),导致维护困难。
3. **类型推导不够友好**:尽管 Vue 2 已经支持 TypeScript,但选项式 API 的类型推导并不够完美。
组合式 API 正是为了解决这些问题而设计的。
## 核心 API 详解
### 1. ref 和 reactive
`ref` 和 `reactive` 是组合式 API 中最基础的状态管理 API。
```javascript
import { ref, reactive } from "vue"
// ref 用于创建基本类型的响应式数据
const count = ref(0)
const message = ref("Hello Vue 3")
// reactive 用于创建对象类型的响应式数据
const state = reactive({
name: "Vue",
version: 3,
features: ["Composition API", "Teleport", "Suspense"]
})
// 在模板中使用时,ref 会自动解包
console.log(count.value) // 0
console.log(state.name) // Vue
```
**重要提示**:`ref` 创建的值需要通过 `.value` 属性来访问和修改,但在模板中会自动解包,无需使用 `.value`。
### 2. computed 计算属性
```javascript
import { ref, computed } from "vue"
const firstName = ref("John")
const lastName = ref("Doe")
// 创建计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 计算属性也是响应式的
console.log(fullName.value) // "John Doe"
```
### 3. watch 和 watchEffect
```javascript
import { ref, watch, watchEffect } from "vue"
const count = ref(0)
// 监听特定数据变化
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// 立即执行,且自动收集依赖
const stop = watchEffect(() => {
console.log(`Current count is: ${count.value}`)
// 当 count 变化时,这个函数会自动重新执行
})
// 停止监听
stop()
```
### 4. 生命周期钩子
组合式 API 中的生命周期钩子需要从 vue 中导入:
```javascript
import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from "vue"
onMounted(() => {
console.log("Component mounted!")
})
onBeforeMount(() => {
console.log("Before mount")
})
```
## 组件通信
### 父子组件通信
```javascript
// ChildComponent.vue
import { defineProps, defineEmits } from "vue"
// 定义props
const props = defineProps({
title: String,
count: {
type: Number,
default: 0
}
})
// 定义emit
const emit = defineEmits(["update", "delete"])
const handleClick = () => {
emit("update", "new value")
}
```
### 依赖注入 Provide/Inject
```javascript
// 父组件
import { provide, ref } from "vue"
const sharedData = ref("Shared data")
provide("sharedKey", sharedData)
// 子组件
import { inject } from "vue"
const data = inject("sharedKey")
console.log(data.value) // "Shared data"
```
## 逻辑复用:Composables
组合式 API 最大的优势之一就是能够轻松实现逻辑复用。通过创建可复用的组合函数(Composables),我们可以将相关逻辑封装在一起。
### 示例:鼠标位置追踪
```javascript
// useMouse.js
import { ref, onMounted, onUnmounted } from "vue"
export function useMouse() {
const x = ref(0)
const y = ref(0)
const updateHandler = (event) => {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => {
window.addEventListener("mousemove", updateHandler)
})
onUnmounted(() => {
window.removeEventListener("mousemove", updateHandler)
})
return { x, y }
}
```
### 使用方式
```javascript
import { useMouse } from "./composables/useMouse"
// 在组件中使用
const { x, y } = useMouse()
console.log(`Mouse position: ${x.value}, ${y.value}`)
```
### 更多实用示例
**useLocalStorage**:本地存储同步
```javascript
// useLocalStorage.js
import { ref, watch } from "vue"
export function useLocalStorage(key, defaultValue) {
const data = ref(localStorage.getItem(key) || defaultValue)
watch(data, (newValue) => {
localStorage.setItem(key, newValue)
}, { deep: true })
return data
}
```
**useFetch**:数据获取
```javascript
// useFetch.js
import { ref } from "vue"
export function useFetch(url) {
const data = ref(null)
const loading = ref(true)
const error = ref(null)
fetch(url)
.then(res => res.json())
.then(json => {
data.value = json
})
.catch(err => {
error.value = err
})
.finally(() => {
loading.value = false
})
return { data, loading, error }
}
```
## 高级特性
### 1. Teleport 传送门
Teleport 允许我们将组件的模板部分渲染到 DOM 的任意位置:
```vue
<template>
<button @click="showModal = true">打开弹窗</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="content">
<p>这是一个模态框</p>
<button @click="showModal = false">关闭</button>
</div>
</div>
</Teleport>
</template>
```
### 2. Suspense 异步组件
Suspense 用于处理异步组件的加载状态:
```vue
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<Loading />
</template>
</Suspense>
</template>
```
### 3. 片段 Fragment
Vue 3 支持组件有多个根节点:
```vue
<template>
<header>Header content</header>
<main>Main content</main>
<footer>Footer content</footer>
</template>
```
## TypeScript 集成
组合式 API 对 TypeScript 提供了出色的支持:
```typescript
import { ref, computed } from "vue"
// 类型推导
const count = ref(0) // Type: Ref<number>
const message = ref("hello") // Type: Ref<string>
// 明确类型
const count2 = ref<number>(0)
// 计算属性类型自动推导
const doubled = computed(() => count.value * 2)
// 泛型函数
function useGeneric<T>(initialValue: T) {
const data = ref<T>(initialValue)
return { data }
}
const { data } = useGeneric<string>("Hello")
```
## 性能优化
### 1. 使用 shallowRef
对于大型对象,使用 `shallowRef` 可以避免深度响应式转换:
```javascript
import { shallowRef, triggerRef } from "vue"
// 只在顶层响应式,不会递归转换
const state = shallowRef({ deep: { nested: "value" } })
// 需要手动触发更新
triggerRef(state)
```
### 2. readonly 保护数据
```javascript
import { readonly, reactive } from "vue"
const original = reactive({ count: 0 })
// 创建只读副本
const copy = readonly(original)
// 尝试修改会收到警告
// copy.count = 1 // 警告!
```
## 迁移策略
从 Vue 2 迁移到 Vue 3 的组合式 API:
1. **渐进式迁移**:可以在同一个组件中混合使用选项式 API 和组合式 API
2. **逐步替换**:先将复杂的逻辑提取为 composables,再逐步改造组件
3. **保持兼容**:Vue 3 完全兼容 Vue 2 的选项式 API
## 最佳实践
1. **保持简洁**:每个 composable 应该有单一职责
2. **命名规范**:以 `use` 开头命名 composables
3. **返回值清晰**:返回必要的响应式引用,避免暴露不必要的内部状态
4. **避免响应式丢失**:不要在解构时丢失响应性
```javascript
// ❌ 错误:解构会丢失响应性
const { x, y } = useMouse()
// ✅ 正确:保持响应性
const { x, y } = toRefs(useMouse())
// 或者
const mouse = useMouse()
const { x, y } = toRefs(mouse)
```
## 总结
Vue 3 的组合式 API 为我们带来了全新的组件开发体验。它不仅解决了长期困扰开发者的逻辑复用问题,还提供了更好的 TypeScript 支持和更灵活的代码组织方式。通过熟练掌握组合式 API,你将能够构建更加健壮、可维护的 Vue 应用程序。
组合式 API 是 Vue 3 最强大的特性之一,也是现代 Vue 开发的必备技能。希望本文能够帮助你更好地理解和应用这一强大工具。
---
**参考资料**:
- Vue 3 官方文档
- Vue Composition API RFC
- Vue 3 迁移指南