引言
Vue.js 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性彻底改变了我们编写 Vue 组件的方式,提供了更灵活、更强大的状态管理和逻辑复用能力。本文将深入探讨 Composition API 的核心概念和使用方法,从基础语法到复杂状态管理方案,为开发者提供一份完整的实战指南。
Vue 3 Composition API 核心概念
什么是 Composition API?
Composition API 是 Vue 3 中引入的一种新的组件开发模式,它允许我们通过组合函数的方式来组织和复用组件逻辑。与传统的 Options API 相比,Composition API 更加灵活,能够更好地处理复杂的业务逻辑和状态管理。
在传统的 Options API 中,我们按照属性、方法、计算属性等将代码分散在不同的选项中。而 Composition API 则允许我们将相关的逻辑组合在一起,使代码更加模块化和可维护。
Composition API 的优势
- 更好的逻辑复用:通过组合函数,可以轻松地在多个组件之间共享逻辑
- 更灵活的代码组织:按照功能而非类型来组织代码
- 更强的类型支持:与 TypeScript 集成更好
- 更好的性能:减少了不必要的渲染和计算
基础语法详解
setup 函数
setup 是 Composition API 的入口函数,它在组件实例创建之前执行。在 setup 函数中,我们可以访问组件的 props、context 以及定义响应式数据。
import { ref, reactive } from 'vue'
export default {
props: {
title: String
},
setup(props, context) {
// 在这里可以访问 props 和 context
console.log(props.title)
console.log(context.emit)
// 定义响应式数据
const count = ref(0)
const user = reactive({
name: 'John',
age: 30
})
return {
count,
user
}
}
}
响应式数据:ref 和 reactive
ref 的使用
ref 用于创建响应式的数据,它会将值包装成一个对象,通过 .value 访问实际值。
import { ref } from 'vue'
export default {
setup() {
// 创建基本类型的响应式数据
const count = ref(0)
const message = ref('Hello Vue')
const isActive = ref(true)
// 修改值
count.value = 10
message.value = 'Hello World'
return {
count,
message,
isActive
}
}
}
reactive 的使用
reactive 用于创建响应式对象,它会将整个对象转换为响应式。
import { reactive } from 'vue'
export default {
setup() {
// 创建响应式对象
const state = reactive({
count: 0,
user: {
name: 'John',
age: 30
},
todos: []
})
// 修改对象属性
state.count = 10
state.user.name = 'Jane'
return {
state
}
}
}
计算属性和侦听器
computed 计算属性
computed 函数用于创建计算属性,它会根据依赖的响应式数据自动计算并缓存结果。
import { ref, computed } from 'vue'
export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')
// 基础计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 带 getter 和 setter 的计算属性
const reversedName = computed({
get: () => {
return firstName.value.split('').reverse().join('')
},
set: (value) => {
firstName.value = value.split('').reverse().join('')
}
})
return {
firstName,
lastName,
fullName,
reversedName
}
}
}
watch 侦听器
watch 函数用于监听响应式数据的变化,当数据发生变化时执行相应的回调函数。
import { ref, watch, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('John')
const user = reactive({
age: 30,
address: {
city: 'Beijing'
}
})
// 监听单个响应式数据
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// 监听多个响应式数据
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
})
// 深度监听对象
watch(user, (newVal, oldVal) => {
console.log('user changed:', newVal)
}, { deep: true })
// 立即执行的侦听器
watch(count, (newVal) => {
console.log('immediate watch:', newVal)
}, { immediate: true })
// watchEffect 会自动追踪其内部使用的响应式数据
watchEffect(() => {
console.log(`current count is: ${count.value}`)
})
return {
count,
name,
user
}
}
}
实际应用案例
创建一个用户管理组件
让我们通过一个实际的用户管理组件来展示 Composition API 的强大功能。
<template>
<div class="user-manager">
<h2>用户管理系统</h2>
<!-- 用户列表 -->
<div class="user-list">
<h3>用户列表</h3>
<ul>
<li
v-for="user in filteredUsers"
:key="user.id"
@click="selectUser(user)"
:class="{ selected: selectedUser && selectedUser.id === user.id }"
>
{{ user.name }} - {{ user.email }}
</li>
</ul>
</div>
<!-- 用户详情 -->
<div class="user-detail" v-if="selectedUser">
<h3>用户详情</h3>
<p><strong>姓名:</strong> {{ selectedUser.name }}</p>
<p><strong>邮箱:</strong> {{ selectedUser.email }}</p>
<p><strong>年龄:</strong> {{ selectedUser.age }}</p>
<p><strong>城市:</strong> {{ selectedUser.address.city }}</p>
<button @click="editUser(selectedUser)">编辑</button>
<button @click="deleteUser(selectedUser.id)">删除</button>
</div>
<!-- 用户表单 -->
<div class="user-form">
<h3>{{ isEditing ? '编辑用户' : '添加新用户' }}</h3>
<form @submit.prevent="handleSubmit">
<input
v-model="formState.name"
placeholder="姓名"
required
/>
<input
v-model="formState.email"
type="email"
placeholder="邮箱"
required
/>
<input
v-model.number="formState.age"
type="number"
placeholder="年龄"
required
/>
<input
v-model="formState.city"
placeholder="城市"
required
/>
<button type="submit">{{ isEditing ? '更新' : '添加' }}</button>
<button type="button" @click="cancelEdit">取消</button>
</form>
</div>
<!-- 搜索和过滤 -->
<div class="controls">
<input
v-model="searchTerm"
placeholder="搜索用户..."
class="search-input"
/>
<select v-model="filterStatus">
<option value="all">全部</option>
<option value="active">活跃</option>
<option value="inactive">非活跃</option>
</select>
</div>
</div>
</template>
<script>
import { ref, reactive, computed, watch } from 'vue'
export default {
name: 'UserManager',
setup() {
// 响应式数据
const users = ref([
{ id: 1, name: '张三', email: 'zhangsan@example.com', age: 25, address: { city: '北京' }, active: true },
{ id: 2, name: '李四', email: 'lisi@example.com', age: 30, address: { city: '上海' }, active: true },
{ id: 3, name: '王五', email: 'wangwu@example.com', age: 28, address: { city: '广州' }, active: false }
])
const selectedUser = ref(null)
const isEditing = ref(false)
// 表单状态
const formState = reactive({
name: '',
email: '',
age: null,
city: ''
})
// 搜索和过滤状态
const searchTerm = ref('')
const filterStatus = ref('all')
// 计算属性
const filteredUsers = computed(() => {
let result = users.value
// 应用搜索过滤
if (searchTerm.value) {
const searchLower = searchTerm.value.toLowerCase()
result = result.filter(user =>
user.name.toLowerCase().includes(searchLower) ||
user.email.toLowerCase().includes(searchLower)
)
}
// 应用状态过滤
if (filterStatus.value !== 'all') {
const isActive = filterStatus.value === 'active'
result = result.filter(user => user.active === isActive)
}
return result
})
// 侦听器
watch(selectedUser, (newUser) => {
if (newUser) {
console.log('用户被选中:', newUser.name)
}
})
// 用户操作方法
const selectUser = (user) => {
selectedUser.value = user
isEditing.value = false
}
const editUser = (user) => {
selectedUser.value = user
isEditing.value = true
// 填充表单数据
formState.name = user.name
formState.email = user.email
formState.age = user.age
formState.city = user.address.city
}
const cancelEdit = () => {
isEditing.value = false
selectedUser.value = null
Object.assign(formState, { name: '', email: '', age: null, city: '' })
}
const handleSubmit = () => {
if (isEditing.value) {
// 更新用户
const userIndex = users.value.findIndex(u => u.id === selectedUser.value.id)
if (userIndex !== -1) {
users.value[userIndex] = {
...users.value[userIndex],
name: formState.name,
email: formState.email,
age: formState.age,
address: { city: formState.city }
}
}
} else {
// 添加新用户
const newUser = {
id: Date.now(),
name: formState.name,
email: formState.email,
age: formState.age,
address: { city: formState.city },
active: true
}
users.value.push(newUser)
}
// 重置表单
cancelEdit()
}
const deleteUser = (userId) => {
if (confirm('确定要删除这个用户吗?')) {
users.value = users.value.filter(user => user.id !== userId)
if (selectedUser.value && selectedUser.value.id === userId) {
selectedUser.value = null
}
}
}
return {
// 数据
users,
selectedUser,
isEditing,
formState,
searchTerm,
filterStatus,
// 计算属性
filteredUsers,
// 方法
selectUser,
editUser,
cancelEdit,
handleSubmit,
deleteUser
}
}
}
</script>
<style scoped>
.user-manager {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.user-list, .user-detail, .user-form {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.user-list ul {
list-style: none;
padding: 0;
}
.user-list li {
padding: 10px;
margin: 5px 0;
cursor: pointer;
background-color: #f5f5f5;
border-radius: 3px;
}
.user-list li:hover,
.user-list li.selected {
background-color: #007bff;
color: white;
}
.controls {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 20px;
}
.search-input {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
form {
display: flex;
flex-direction: column;
gap: 10px;
}
input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
button {
padding: 8px 16px;
border: none;
border-radius: 3px;
cursor: pointer;
}
button[type="submit"] {
background-color: #28a745;
color: white;
}
button[type="button"] {
background-color: #6c757d;
color: white;
}
</style>
复杂状态管理方案
使用组合函数进行逻辑复用
在大型应用中,我们经常需要将复杂的业务逻辑抽象成可复用的组合函数。
// composables/useLocalStorage.js
import { ref, watch } from 'vue'
export function useLocalStorage(key, defaultValue) {
const storedValue = localStorage.getItem(key)
const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return value
}
// composables/useApi.js
import { ref, reactive } from 'vue'
export function useApi() {
const loading = ref(false)
const error = ref(null)
const data = ref(null)
const request = async (apiCall) => {
try {
loading.value = true
error.value = null
const result = await apiCall()
data.value = result
return result
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
return {
loading,
error,
data,
request
}
}
// composables/useUser.js
import { ref, computed } from 'vue'
import { useLocalStorage } from './useLocalStorage'
export function useUser() {
const user = useLocalStorage('user', null)
const isLoggedIn = computed(() => !!user.value)
const login = (userData) => {
user.value = userData
}
const logout = () => {
user.value = null
}
return {
user,
isLoggedIn,
login,
logout
}
}
状态管理的最佳实践
创建全局状态管理
// store/index.js
import { reactive, readonly } from 'vue'
const state = reactive({
count: 0,
user: null,
theme: 'light',
notifications: []
})
const mutations = {
INCREMENT(state) {
state.count++
},
SET_USER(state, user) {
state.user = user
},
SET_THEME(state, theme) {
state.theme = theme
},
ADD_NOTIFICATION(state, notification) {
state.notifications.push(notification)
}
}
const actions = {
increment({ commit }) {
commit('INCREMENT')
},
setUser({ commit }, user) {
commit('SET_USER', user)
},
setTheme({ commit }, theme) {
commit('SET_THEME', theme)
},
addNotification({ commit }, notification) {
commit('ADD_NOTIFICATION', notification)
}
}
export const useStore = () => {
const store = {
state: readonly(state),
...actions
}
return store
}
在组件中使用全局状态
<script>
import { computed } from 'vue'
import { useStore } from '@/store'
export default {
setup() {
const store = useStore()
const count = computed(() => store.state.count)
const user = computed(() => store.state.user)
const increment = () => {
store.increment()
}
const setUser = (userData) => {
store.setUser(userData)
}
return {
count,
user,
increment,
setUser
}
}
}
</script>
性能优化技巧
合理使用 computed 和 watch
// 避免在计算属性中进行复杂计算
import { ref, computed } from 'vue'
export default {
setup() {
const items = ref([])
// 好的做法:简单的计算
const itemCount = computed(() => items.value.length)
// 好的做法:缓存复杂计算结果
const expensiveCalculation = computed(() => {
return items.value.reduce((sum, item) => {
// 复杂的计算逻辑
return sum + (item.price * item.quantity)
}, 0)
})
return {
items,
itemCount,
expensiveCalculation
}
}
}
使用 watchEffect 优化性能
import { ref, watchEffect } from 'vue'
export default {
setup() {
const firstName = ref('')
const lastName = ref('')
const fullName = ref('')
// 使用 watchEffect 自动追踪依赖
watchEffect(() => {
fullName.value = `${firstName.value} ${lastName.value}`
})
return {
firstName,
lastName,
fullName
}
}
}
与 Vue 2 的对比
Options API vs Composition API
| 特性 | Options API | Composition API |
|---|---|---|
| 代码组织 | 按属性类型分组 | 按功能逻辑分组 |
| 逻辑复用 | Mixins, extends | 组合函数 |
| 类型支持 | 较弱 | 更好 |
| 性能 | 较高 | 相当或更高 |
| 学习曲线 | 平缓 | 较陡峭 |
迁移建议
- 渐进式迁移:可以同时使用两种 API
- 优先重构复杂组件:优先考虑业务逻辑复杂的组件
- 保持一致性:团队内保持使用统一的开发模式
- 充分测试:迁移后进行全面的测试
常见问题和解决方案
1. 响应式数据丢失问题
// 错误做法
export default {
setup() {
const user = reactive({ name: 'John' })
// 直接替换整个对象,会导致响应式丢失
user = { name: 'Jane' } // 这样做会破坏响应式
return { user }
}
}
// 正确做法
export default {
setup() {
const user = reactive({ name: 'John' })
// 正确地修改属性
user.name = 'Jane'
// 或者使用 Vue.set 或者直接赋值给响应式对象的属性
// 如果需要替换整个对象,可以重新创建
const updateUser = (newUser) => {
Object.assign(user, newUser)
}
return { user, updateUser }
}
}
2. 异步数据处理
import { ref, watch } from 'vue'
export default {
setup() {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const fetchData = async (url) => {
try {
loading.value = true
error.value = null
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const result = await response.json()
data.value = result
} catch (err) {
error.value = err.message
console.error('Fetch error:', err)
} finally {
loading.value = false
}
}
// 监听数据变化
watch(data, (newData) => {
if (newData) {
console.log('Data updated:', newData)
}
})
return {
data,
loading,
error,
fetchData
}
}
}
总结
Vue 3 的 Composition API 为前端开发带来了革命性的变化。通过本文的详细介绍,我们了解了:
- 基础语法:setup 函数、ref 和 reactive 的使用
- 核心特性:计算属性、侦听器的实现方式
- 实际应用:创建完整的用户管理组件
- 复杂状态管理:组合函数、全局状态管理的最佳实践
- 性能优化:合理的使用策略和性能提升技巧
Composition API 的引入让 Vue 组件更加灵活和可维护,特别是在处理复杂的业务逻辑时表现尤为突出。通过合理使用组合函数,我们可以轻松实现逻辑复用,提高开发效率。
在实际项目中,建议:
- 从简单的组件开始尝试 Composition API
- 逐步将复杂的逻辑抽象成可复用的组合函数
- 注意性能优化,避免不必要的计算和监听
- 充分利用 TypeScript 的类型支持
随着 Vue 生态系统的不断发展,Composition API 将会成为 Vue 开发的标准模式。掌握这一技术不仅能够提升开发效率,还能让我们编写出更加优雅和可维护的代码。
通过本文的实战指南,希望读者能够在实际项目中熟练运用 Composition API,构建出更加优秀的 Vue 应用程序。

评论 (0)