前言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。作为 Vue 生态系统的重要升级,Composition API 不仅解决了 Vue 2 中 Options API 的诸多限制,还为开发者提供了更灵活、更强大的状态管理和组件构建方式。本文将深入探讨 Composition API 的各个方面,从基础概念到高级实践,帮助开发者全面掌握这一重要技术。
什么是 Composition API
核心概念
Composition API 是 Vue 3 中引入的一种新的组件状态管理方式,它允许开发者以函数的形式组织和复用逻辑代码,而不是传统的选项式 API。与 Vue 2 中的 Options API 相比,Composition API 提供了更灵活的代码组织方式,使得复杂组件的状态管理变得更加清晰和可维护。
与 Options API 的对比
在 Vue 2 中,我们通常使用 data、methods、computed、watch 等选项来组织组件逻辑。这种方式在简单组件中表现良好,但当组件变得复杂时,相关的逻辑会被分散到不同的选项中,导致代码难以维护和复用。
// Vue 2 Options API 示例
export default {
data() {
return {
count: 0,
name: ''
}
},
computed: {
reversedName() {
return this.name.split('').reverse().join('')
}
},
methods: {
increment() {
this.count++
},
reset() {
this.count = 0
}
},
watch: {
count(newVal, oldVal) {
console.log(`count changed from ${oldVal} to ${newVal}`)
}
}
}
而 Composition API 则将相关的逻辑组织在一起,使代码更加直观:
// Vue 3 Composition API 示例
import { ref, computed, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('')
const reversedName = computed(() => {
return name.value.split('').reverse().join('')
})
const increment = () => {
count.value++
}
const reset = () => {
count.value = 0
}
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
return {
count,
name,
reversedName,
increment,
reset
}
}
}
响应式数据管理
ref 和 reactive 的基本使用
在 Composition API 中,响应式数据的创建主要通过 ref 和 reactive 两个核心函数来实现。
ref 的使用
ref 用于创建响应式的原始值引用。对于基本数据类型(字符串、数字、布尔值等),应该使用 ref:
import { ref } from 'vue'
export default {
setup() {
// 创建一个响应式的数字
const count = ref(0)
// 创建一个响应式的字符串
const message = ref('Hello Vue')
// 创建一个响应式的布尔值
const isActive = ref(true)
const increment = () => {
count.value++ // 注意:访问时需要使用 .value
}
return {
count,
message,
isActive,
increment
}
}
}
reactive 的使用
reactive 用于创建响应式的对象。对于复杂的数据结构(对象、数组等),应该使用 reactive:
import { reactive } from 'vue'
export default {
setup() {
// 创建一个响应式对象
const user = reactive({
name: 'John',
age: 30,
email: 'john@example.com'
})
// 创建一个响应式数组
const items = reactive([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
])
const updateUser = (newName) => {
user.name = newName
}
const addItem = (item) => {
items.push(item)
}
return {
user,
items,
updateUser,
addItem
}
}
}
响应式数据的访问和修改
在 Composition API 中,访问响应式数据需要使用 .value 属性,这是与 Vue 2 的一个重要区别:
import { ref, reactive } from 'vue'
export default {
setup() {
const count = ref(0)
const userInfo = reactive({
name: 'Alice',
age: 25
})
// 访问响应式数据
console.log(count.value) // 0
console.log(userInfo.name) // Alice
// 修改响应式数据
const increment = () => {
count.value++ // 正确方式
// count++ // 错误!不会触发响应式更新
}
const updateUserInfo = () => {
userInfo.age = 26 // 对于 reactive 对象,直接赋值即可
}
return {
count,
userInfo,
increment,
updateUserInfo
}
}
}
深层嵌套响应式数据
对于深层嵌套的对象,Vue 3 会自动将所有嵌套的属性转换为响应式:
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
user: {
profile: {
name: 'Bob',
settings: {
theme: 'dark',
notifications: true
}
}
}
})
const updateUserTheme = () => {
// 这种修改会触发响应式更新
state.user.profile.settings.theme = 'light'
}
return {
state,
updateUserTheme
}
}
}
组合式函数复用
创建可复用的组合式函数
组合式函数是 Composition API 的核心特性之一,它允许我们将逻辑封装成可复用的函数:
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = initialValue
}
return {
count,
increment,
decrement,
reset
}
}
// composables/useFetch.js
import { ref, watch } from 'vue'
export function useFetch(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const fetchData = async () => {
try {
loading.value = true
error.value = null
const response = await fetch(url)
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
// 在组件挂载时自动获取数据
watch(() => url, fetchData, { immediate: true })
return {
data,
loading,
error,
fetchData
}
}
使用组合式函数
// MyComponent.vue
import { defineComponent } from 'vue'
import { useCounter } from '@/composables/useCounter'
import { useFetch } from '@/composables/useFetch'
export default defineComponent({
name: 'MyComponent',
setup() {
// 使用计数器组合式函数
const { count, increment, decrement, reset } = useCounter(10)
// 使用数据获取组合式函数
const { data, loading, error, fetchData } = useFetch('/api/users')
return {
count,
increment,
decrement,
reset,
data,
loading,
error,
fetchData
}
}
})
高级组合式函数示例
// 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/useWindowScroll.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useWindowScroll() {
const scrollY = ref(0)
const handleScroll = () => {
scrollY.value = window.scrollY
}
onMounted(() => {
window.addEventListener('scroll', handleScroll)
// 初始化值
handleScroll()
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
return { scrollY }
}
组件状态管理
状态的组织和管理
在大型应用中,合理组织组件状态至关重要。使用 Composition API 可以更好地管理复杂的状态:
// composables/useUserStore.js
import { ref, computed } from 'vue'
export function useUserStore() {
const users = ref([])
const loading = ref(false)
const error = ref(null)
// 计算属性:获取活跃用户数量
const activeUsersCount = computed(() => {
return users.value.filter(user => user.isActive).length
})
// 计算属性:按名称排序的用户列表
const sortedUsers = computed(() => {
return [...users.value].sort((a, b) => a.name.localeCompare(b.name))
})
// 方法:添加用户
const addUser = (user) => {
users.value.push(user)
}
// 方法:更新用户
const updateUser = (id, updates) => {
const index = users.value.findIndex(user => user.id === id)
if (index !== -1) {
users.value[index] = { ...users.value[index], ...updates }
}
}
// 方法:删除用户
const deleteUser = (id) => {
users.value = users.value.filter(user => user.id !== id)
}
return {
users,
loading,
error,
activeUsersCount,
sortedUsers,
addUser,
updateUser,
deleteUser
}
}
状态管理的最佳实践
// components/UserList.vue
import { defineComponent, onMounted } from 'vue'
import { useUserStore } from '@/composables/useUserStore'
export default defineComponent({
name: 'UserList',
setup() {
const { users, loading, error, activeUsersCount, sortedUsers, addUser } = useUserStore()
// 在组件挂载时初始化数据
onMounted(() => {
// 模拟加载数据
loadData()
})
const loadData = async () => {
try {
loading.value = true
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000))
users.value = [
{ id: 1, name: 'Alice', email: 'alice@example.com', isActive: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', isActive: false },
{ id: 3, name: 'Charlie', email: 'charlie@example.com', isActive: true }
]
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
const handleAddUser = () => {
addUser({
id: Date.now(),
name: `User ${users.value.length + 1}`,
email: `user${users.value.length + 1}@example.com`,
isActive: true
})
}
return {
users,
loading,
error,
activeUsersCount,
sortedUsers,
handleAddUser
}
}
})
性能优化策略
深度响应式与浅层响应式
Vue 3 提供了不同的响应式创建方式来优化性能:
import { ref, reactive, shallowRef, shallowReactive } from 'vue'
export default {
setup() {
// 深层响应式 - 默认行为
const deepObject = reactive({
nested: {
value: 1,
data: [1, 2, 3]
}
})
// 浅层响应式 - 只响应顶层属性的变化
const shallowObject = shallowReactive({
nested: {
value: 1,
data: [1, 2, 3]
}
})
// 浅层 ref - 只响应顶层值的变化
const shallowRefValue = shallowRef({
nested: {
value: 1,
data: [1, 2, 3]
}
})
return {
deepObject,
shallowObject,
shallowRefValue
}
}
}
计算属性优化
合理的计算属性使用可以显著提升性能:
import { ref, computed } from 'vue'
export default {
setup() {
const items = ref([])
const filterText = ref('')
// 高效的计算属性 - 只在依赖变化时重新计算
const filteredItems = computed(() => {
if (!filterText.value) return items.value
return items.value.filter(item =>
item.name.toLowerCase().includes(filterText.value.toLowerCase())
)
})
// 复杂计算的优化版本
const expensiveComputed = computed({
get: () => {
// 复杂的计算逻辑
return items.value.reduce((acc, item) => {
acc[item.category] = (acc[item.category] || 0) + item.value
return acc
}, {})
},
set: (newValue) => {
// 设置值的逻辑
items.value = newValue.items
}
})
return {
items,
filterText,
filteredItems,
expensiveComputed
}
}
}
组件渲染优化
import { defineComponent, shallowRef, markRaw } from 'vue'
export default defineComponent({
name: 'OptimizedComponent',
setup() {
// 使用 shallowRef 避免不必要的响应式追踪
const shallowData = shallowRef({
// 大量数据,不需要响应式
largeArray: new Array(1000).fill(0).map((_, i) => ({ id: i, value: Math.random() }))
})
// 使用 markRaw 避免对象被转换为响应式
const rawObject = markRaw({
// 一些不需要响应式的对象
config: {
version: '1.0.0',
features: ['feature1', 'feature2']
}
})
return {
shallowData,
rawObject
}
}
})
动态导入和懒加载
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
name: 'LazyLoadedComponent',
setup() {
const component = ref(null)
const loading = ref(false)
const loadComponent = async () => {
try {
loading.value = true
// 动态导入组件
const { default: LazyComponent } = await import('./LazyComponent.vue')
component.value = LazyComponent
} catch (error) {
console.error('Failed to load component:', error)
} finally {
loading.value = false
}
}
onMounted(() => {
// 延迟加载组件
setTimeout(() => {
loadComponent()
}, 1000)
})
return {
component,
loading
}
}
})
高级技巧和最佳实践
自定义指令与 Composition API 结合
// directives/focus.js
import { onMounted, onUnmounted } from 'vue'
export default {
mounted(el, binding, vnode) {
const focus = () => {
el.focus()
}
// 使用 Composition API 的方式
if (binding.value === true) {
focus()
}
},
updated(el, binding) {
if (binding.value === true) {
el.focus()
}
}
}
异步组件与性能监控
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
name: 'PerformanceMonitor',
setup() {
const startTime = performance.now()
const metrics = ref({
renderTime: 0,
loadTime: 0
})
const measurePerformance = () => {
const endTime = performance.now()
metrics.value.renderTime = endTime - startTime
// 发送性能数据到监控服务
console.log('Render time:', metrics.value.renderTime, 'ms')
}
onMounted(() => {
// 组件挂载时测量性能
setTimeout(measurePerformance, 0)
})
return {
metrics
}
}
})
状态持久化和恢复
// composables/usePersistentState.js
import { ref, watch } from 'vue'
export function usePersistentState(key, defaultValue) {
// 从 localStorage 恢复状态
const storedValue = localStorage.getItem(key)
const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
// 监听状态变化并保存到 localStorage
watch(value, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
// 提供重置功能
const reset = () => {
value.value = defaultValue
localStorage.removeItem(key)
}
return {
value,
reset
}
}
实战案例:构建一个完整的用户管理系统
项目结构设计
// src/composables/useUserManagement.js
import { ref, computed, watch } from 'vue'
export function useUserManagement() {
// 响应式状态
const users = ref([])
const loading = ref(false)
const error = ref(null)
const searchQuery = ref('')
const selectedUser = ref(null)
// 计算属性
const filteredUsers = computed(() => {
if (!searchQuery.value) return users.value
const query = searchQuery.value.toLowerCase()
return users.value.filter(user =>
user.name.toLowerCase().includes(query) ||
user.email.toLowerCase().includes(query)
)
})
const activeUsers = computed(() => {
return users.value.filter(user => user.isActive)
})
// 方法
const fetchUsers = async () => {
try {
loading.value = true
error.value = null
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000))
users.value = [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com', isActive: true, role: 'admin' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com', isActive: false, role: 'user' },
{ id: 3, name: 'Charlie Brown', email: 'charlie@example.com', isActive: true, role: 'user' }
]
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
const createUser = async (userData) => {
try {
loading.value = true
const newUser = {
id: Date.now(),
...userData,
isActive: true
}
users.value.push(newUser)
return newUser
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
const updateUser = async (id, userData) => {
try {
loading.value = true
const index = users.value.findIndex(user => user.id === id)
if (index !== -1) {
users.value[index] = { ...users.value[index], ...userData }
return users.value[index]
}
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
const deleteUser = async (id) => {
try {
loading.value = true
users.value = users.value.filter(user => user.id !== id)
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
// 监听搜索查询变化
watch(searchQuery, () => {
console.log('Search query changed:', searchQuery.value)
})
return {
users,
loading,
error,
searchQuery,
selectedUser,
filteredUsers,
activeUsers,
fetchUsers,
createUser,
updateUser,
deleteUser
}
}
组件实现
<!-- src/components/UserManagement.vue -->
<template>
<div class="user-management">
<div class="header">
<h2>用户管理</h2>
<div class="search-bar">
<input
v-model="searchQuery"
placeholder="搜索用户..."
class="search-input"
/>
<button @click="fetchUsers" :disabled="loading">
{{ loading ? '加载中...' : '刷新' }}
</button>
</div>
</div>
<div class="stats">
<span>总用户数: {{ users.length }}</span>
<span>活跃用户: {{ activeUsers.length }}</span>
</div>
<div v-if="error" class="error-message">
错误: {{ error }}
</div>
<div v-if="loading" class="loading">
加载中...
</div>
<div v-else class="users-list">
<div
v-for="user in filteredUsers"
:key="user.id"
class="user-card"
@click="selectUser(user)"
>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<span class="status" :class="{ active: user.isActive }">
{{ user.isActive ? '活跃' : '非活跃' }}
</span>
<span class="role">{{ user.role }}</span>
</div>
</div>
<div v-if="selectedUser" class="user-details">
<h3>用户详情</h3>
<p><strong>姓名:</strong> {{ selectedUser.name }}</p>
<p><strong>邮箱:</strong> {{ selectedUser.email }}</p>
<p><strong>状态:</strong> {{ selectedUser.isActive ? '活跃' : '非活跃' }}</p>
<p><strong>角色:</strong> {{ selectedUser.role }}</p>
<button @click="selectedUser = null">关闭</button>
</div>
</div>
</template>
<script>
import { defineComponent, onMounted } from 'vue'
import { useUserManagement } from '@/composables/useUserManagement'
export default defineComponent({
name: 'UserManagement',
setup() {
const {
users,
loading,
error,
searchQuery,
selectedUser,
filteredUsers,
activeUsers,
fetchUsers
} = useUserManagement()
const selectUser = (user) => {
selectedUser.value = user
}
onMounted(() => {
fetchUsers()
})
return {
users,
loading,
error,
searchQuery,
selectedUser,
filteredUsers,
activeUsers,
fetchUsers,
selectUser
}
}
})
</script>
<style scoped>
.user-management {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 15px;
}
.search-bar {
display: flex;
gap: 10px;
align-items: center;
}
.search-input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
min-width: 200px;
}
.stats {
display: flex;
gap: 20px;
margin-bottom: 20px;
font-weight: bold;
}
.users-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 15px;
}
.user-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
}
.user-card:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 8px;
}
.status.active {
background-color: #d4edda;
color: #155724;
}
.role {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
background-color: #f8f9fa;
color: #6c757d;
}
.error-message {
color: #dc3545;
padding: 10px;
margin-bottom: 20px;
border-radius: 4px;
background-color: #f8d7da;
}
.loading {
text-align: center;
padding: 20px;
font-size: 18px;
}
.user-details {
margin-top: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
</style>
总结
Vue 3 的 Composition API 为前端开发带来了革命性的变化,它不仅解决了 Vue 2 中 Options API 的诸多限制,还提供了更灵活、更强大的状态管理和组件构建方式。通过本文的详细介绍,我们可以看到:
- 响应式数据管理:
ref和reactive提供了灵活的数据响应式创建方式 - 组合式函数复用:将逻辑封装成可复用的函数,提高代码的可维护性
- 组件状态管理:合理组织和管理复杂的状态逻辑
- 性能优化策略:通过深层/浅层响应式、计算属性优化等手段提升应用性能
在实际开发中,建议:
- 根据项目复杂度选择合适的 API 风格
- 合理使用组合式函数来复用逻辑
- 注意性能优化,避免不必要的响应式追踪
- 建立良好的代码组织结构和命名

评论 (0)