Vue3 组合式 API 实战:从入门到精通的完整指南
一、背景与动机
Vue3 的发布带来了革命性的变化,其中最引人注目的莫过于组合式 API(Composition API)。在 Vue2 时代,我们习惯使用选项式 API(Options API)来组织代码,但随着项目规模的增长,这种方式逐渐暴露出一些问题:逻辑复用困难、代码组织分散、TypeScript 支持不够友好等。
组合式 API 的出现正是为了解决这些痛点。它允许我们按照逻辑关注点组织代码,而不是按照选项类型分散在 data、methods、computed 等不同区域。这不仅提高了代码的可读性和可维护性,还为逻辑复用提供了更优雅的解决方案。
二、核心概念详解
2.1 响应式系统
组合式 API 的核心是响应式系统,主要由 ref 和 reactive 两个函数构成。
ref 的使用:
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const message = ref('Hello Vue3')
// 在 setup 中需要 .value 访问
console.log(count.value) // 0
// 在模板中自动解包,无需 .value
return { count, message }
}
}
reactive 的使用:
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
user: {
name: '张三',
age: 25,
skills: ['Vue', 'React', 'TypeScript']
},
loading: false,
error: null
})
// 直接访问属性,无需 .value
console.log(state.user.name) // 张三
return { state }
}
}
2.2 setup 函数
setup 是组合式 API 的入口点,在组件创建之前执行,是定义响应式状态、计算属性、方法和生命周期钩子的地方。
<script setup>
import { ref, computed, onMounted } from 'vue'
// 定义响应式状态
const count = ref(0)
const doubledCount = computed(() => count.value * 2)
// 定义方法
const increment = () => {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('组件已挂载')
})
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubledCount }}</p>
<button @click="increment">增加</button>
</div>
</template>
2.3 生命周期钩子
组合式 API 提供了与选项式 API 对应的生命周期钩子函数:
| 选项式 API | 组合式 API |
|---|---|
| beforeCreate | setup() 本身 |
| created | setup() 本身 |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
三、实战案例:用户管理系统
下面通过一个完整的用户管理模块来展示组合式 API 的最佳实践。
3.1 逻辑复用:自定义 Composable
// composables/useUser.js
import { ref, computed } from 'vue'
import { fetchUsers, createUser, updateUser, deleteUser } from '@/api/user'
export function useUser() {
const users = ref([])
const loading = ref(false)
const error = ref(null)
const selectedUser = ref(null)
// 计算属性
const activeUsers = computed(() =>
users.value.filter(user => user.status === 'active')
)
const userCount = computed(() => users.value.length)
// 获取用户列表
const loadUsers = async () => {
loading.value = true
error.value = null
try {
const response = await fetchUsers()
users.value = response.data
} catch (e) {
error.value = e.message || '获取用户列表失败'
} finally {
loading.value = false
}
}
// 创建用户
const addUser = async (userData) => {
loading.value = true
try {
const response = await createUser(userData)
users.value.push(response.data)
return { success: true, data: response.data }
} catch (e) {
error.value = e.message || '创建用户失败'
return { success: false, error: error.value }
} finally {
loading.value = false
}
}
// 更新用户
const editUser = async (id, userData) => {
loading.value = true
try {
const response = await updateUser(id, userData)
const index = users.value.findIndex(u => u.id === id)
if (index !== -1) {
users.value[index] = response.data
}
return { success: true, data: response.data }
} catch (e) {
error.value = e.message || '更新用户失败'
return { success: false, error: error.value }
} finally {
loading.value = false
}
}
// 删除用户
const removeUser = async (id) => {
loading.value = true
try {
await deleteUser(id)
users.value = users.value.filter(u => u.id !== id)
return { success: true }
} catch (e) {
error.value = e.message || '删除用户失败'
return { success: false, error: error.value }
} finally {
loading.value = false
}
}
return {
// 状态
users,
loading,
error,
selectedUser,
// 计算属性
activeUsers,
userCount,
// 方法
loadUsers,
addUser,
editUser,
removeUser
}
}
3.2 组件中使用 Composable
<!-- components/UserManager.vue -->
<script setup>
import { onMounted } from 'vue'
import { useUser } from '@/composables/useUser'
import UserForm from './UserForm.vue'
import UserList from './UserList.vue'
const {
users,
loading,
error,
activeUsers,
userCount,
loadUsers,
addUser,
editUser,
removeUser
} = useUser()
// 初始化加载
onMounted(() => {
loadUsers()
})
// 处理表单提交
const handleSubmit = async (formData) => {
const result = formData.id
? await editUser(formData.id, formData)
: await addUser(formData)
if (result.success) {
// 显示成功提示
console.log('操作成功')
}
}
// 处理删除
const handleDelete = async (id) => {
if (confirm('确定要删除该用户吗?')) {
const result = await removeUser(id)
if (result.success) {
console.log('删除成功')
}
}
}
</script>
<template>
<div class="user-manager">
<header>
<h1>用户管理</h1>
<p>总用户数: {{ userCount }} | 活跃用户: {{ activeUsers.length }}</p>
</header>
<UserForm @submit="handleSubmit" />
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<UserList v-else :users="users" @edit="handleSubmit" @delete="handleDelete" />
</div>
</template>
四、最佳实践总结
4.1 ref vs reactive 的选择
- 使用 ref:当需要处理基本类型(string、number、boolean)或需要重新分配整个对象时
- 使用 reactive:当处理复杂对象,且不需要重新分配整个对象时
- 推荐做法:团队内保持一致性,建议优先使用 ref,因为解构时不会丢失响应性
4.2 Composable 命名与组织
- 以
use开头命名,如useUser、useCart - 单一职责:每个 Composable 只负责一个逻辑关注点
- 返回值使用对象,便于按需解构
- 将 Composable 放在
composables/或hooks/目录下
4.3 性能优化技巧
// 使用 shallowRef 避免深度响应
import { shallowRef } from 'vue'
const largeData = shallowRef([])
// 使用 computed 缓存计算结果
const filteredList = computed(() =>
list.value.filter(item => item.active)
)
// 使用 watchEffect 自动追踪依赖
watchEffect(() => {
// 自动收集依赖,依赖变化时自动执行
localStorage.setItem('theme', theme.value)
})
五、常见问题与解决方案
5.1 解构失去响应性
// ❌ 错误:解构后失去响应性
const { name, age } = reactive({ name: '张三', age: 25 })
// ✅ 正确:使用 toRefs
import { toRefs } from 'vue'
const state = reactive({ name: '张三', age: 25 })
const { name, age } = toRefs(state)
5.2 在 setup 中访问组件实例
组合式 API 中没有 this,如果需要访问组件实例,可以使用 getCurrentInstance:
import { getCurrentInstance } from 'vue'
export default {
setup() {
const instance = getCurrentInstance()
// 仅在必要时使用,不推荐过度依赖
}
}
5.3 响应式丢失排查
使用 isRef 和 isReactive 工具函数进行调试:
import { isRef, isReactive, ref, reactive } from 'vue'
const count = ref(0)
const state = reactive({ name: 'test' })
console.log(isRef(count)) // true
console.log(isReactive(state)) // true
六、总结
Vue3 组合式 API 是一次重大的范式转变,它带来的好处包括:
- 更好的逻辑复用:通过 Composable 可以轻松提取和复用逻辑
- 更灵活的代码组织:按功能而非选项类型组织代码
- 更好的 TypeScript 支持:完整的类型推断
- 更小的打包体积:Tree-shaking 友好
- 更直观的代码阅读体验:相关代码聚合在一起
掌握组合式 API 是成为 Vue3 高手的必经之路。建议从小型组件开始尝试,逐步将选项式 API 的心智模型转换为组合式 API 的思维模式。相信随着实践的深入,你会发现组合式 API 的优雅和强大。
0 评论
评论区
登录 后参与评论