引言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性不仅改变了我们编写 Vue 组件的方式,还为开发者提供了更灵活、更强大的开发模式。相比于 Vue 2 中的选项式 API,Composition API 更加注重逻辑复用和代码组织,让组件变得更加模块化和可维护。
在本文中,我们将深入探讨 Vue 3 Composition API 的核心概念和使用技巧,从基础的响应式数据管理到高级的组件逻辑复用,再到状态管理的最佳实践。通过详细的代码示例和实际应用场景,帮助开发者掌握这一现代前端开发的重要工具。
Composition API 核心概念
响应式数据管理
Composition API 的核心在于对响应式数据的管理。在 Vue 3 中,我们可以通过 ref 和 reactive 来创建响应式数据:
import { ref, reactive } from 'vue'
// 使用 ref 创建响应式数据
const count = ref(0)
const name = ref('Vue')
// 使用 reactive 创建响应式对象
const state = reactive({
count: 0,
name: 'Vue',
user: {
firstName: 'John',
lastName: 'Doe'
}
})
// 在模板中使用
// <template>
// <p>{{ count }}</p>
// <p>{{ name }}</p>
// <p>{{ state.count }}</p>
// </template>
响应式数据的访问和修改
在 Composition API 中,访问响应式数据需要通过 .value 属性:
import { ref, reactive } from 'vue'
const count = ref(0)
const userInfo = reactive({
name: 'John',
age: 25
})
// 修改数据
count.value = 10
userInfo.name = 'Jane'
userInfo.age = 30
// 在模板中访问
// <template>
// <p>{{ count }}</p>
// <p>{{ userInfo.name }}</p>
// </template>
计算属性和监听器
Composition API 提供了 computed 和 watch 来处理计算属性和监听器:
import { ref, computed, watch } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// 计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 监听器
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// 监听多个数据源
watch([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName]) => {
console.log(`Name changed from ${oldFirstName} ${oldLastName} to ${newFirstName} ${newLastName}`)
})
组件逻辑复用
自定义组合式函数
组合式函数是 Vue 3 Composition API 的核心特性之一,它允许我们将可复用的逻辑封装成独立的函数:
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = initialValue
}
const doubleCount = computed(() => count.value * 2)
return {
count,
increment,
decrement,
reset,
doubleCount
}
}
// 在组件中使用
import { useCounter } from '@/composables/useCounter'
export default {
setup() {
const { count, increment, decrement, reset, doubleCount } = useCounter(10)
return {
count,
increment,
decrement,
reset,
doubleCount
}
}
}
复杂逻辑的封装
让我们来看一个更复杂的例子,封装一个数据获取和加载状态管理的组合式函数:
// composables/useApi.js
import { ref, computed } from 'vue'
export function useApi(apiFunction, initialData = null) {
const data = ref(initialData)
const loading = ref(false)
const error = ref(null)
const fetchData = async (...args) => {
try {
loading.value = true
error.value = null
const result = await apiFunction(...args)
data.value = result
} catch (err) {
error.value = err
console.error('API Error:', err)
} finally {
loading.value = false
}
}
const hasData = computed(() => data.value !== null && data.value !== undefined)
return {
data,
loading,
error,
fetchData,
hasData
}
}
// 在组件中使用
import { useApi } from '@/composables/useApi'
export default {
setup() {
const { data, loading, error, fetchData, hasData } = useApi(fetchUserData, null)
// 初始化时获取数据
fetchData('user/123')
return {
data,
loading,
error,
fetchData,
hasData
}
}
}
状态共享和通信
通过组合式函数,我们可以在不同组件之间共享状态:
// composables/useGlobalState.js
import { reactive } from 'vue'
const globalState = reactive({
user: null,
theme: 'light',
language: 'zh-CN'
})
export function useGlobalState() {
const setUser = (user) => {
globalState.user = user
}
const setTheme = (theme) => {
globalState.theme = theme
}
const setLanguage = (language) => {
globalState.language = language
}
return {
state: globalState,
setUser,
setTheme,
setLanguage
}
}
// 在组件中使用
import { useGlobalState } from '@/composables/useGlobalState'
export default {
setup() {
const { state, setUser, setTheme } = useGlobalState()
return {
user: state.user,
theme: state.theme,
setUser,
setTheme
}
}
}
生命周期钩子管理
setup 函数详解
setup 是 Composition API 的入口函数,它在组件实例创建之前执行:
import { ref, onMounted, onUpdated, onUnmounted } from 'vue'
export default {
setup(props, context) {
// 组件属性和上下文访问
console.log('Props:', props)
console.log('Context:', context)
const count = ref(0)
const name = ref('')
// 在组件挂载后执行
onMounted(() => {
console.log('Component mounted')
// 初始化数据或订阅事件
fetchInitialData()
})
// 在组件更新后执行
onUpdated(() => {
console.log('Component updated')
// 处理更新后的逻辑
})
// 在组件销毁前执行
onUnmounted(() => {
console.log('Component unmounted')
// 清理资源,取消订阅等
cleanup()
})
const fetchInitialData = async () => {
// 模拟数据获取
await new Promise(resolve => setTimeout(resolve, 1000))
name.value = 'Vue 3'
}
const cleanup = () => {
// 清理定时器、事件监听器等
}
return {
count,
name
}
}
}
响应式生命周期钩子
除了传统的生命周期钩子,Vue 3 还提供了更灵活的响应式生命周期管理:
import { ref, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('')
// watchEffect 会自动追踪依赖
watchEffect(() => {
console.log(`Count: ${count.value}, Name: ${name.value}`)
// 这个副作用函数会在 count 或 name 变化时重新执行
})
// 带有清理功能的 watchEffect
const cleanup = watchEffect((onInvalidate) => {
const timer = setTimeout(() => {
console.log('Timer executed')
}, 1000)
// 注册清理函数
onInvalidate(() => {
clearTimeout(timer)
console.log('Timer cleared')
})
})
return {
count,
name
}
}
}
状态管理最佳实践
基于组合式函数的状态管理
我们可以创建一个完整的状态管理解决方案:
// stores/useUserStore.js
import { ref, computed } from 'vue'
export function useUserStore() {
const users = ref([])
const currentUser = ref(null)
const loading = ref(false)
// 获取用户列表
const fetchUsers = async () => {
try {
loading.value = true
const response = await fetch('/api/users')
const data = await response.json()
users.value = data
} catch (error) {
console.error('Failed to fetch users:', error)
} finally {
loading.value = false
}
}
// 获取当前用户
const fetchCurrentUser = async () => {
try {
loading.value = true
const response = await fetch('/api/user')
const data = await response.json()
currentUser.value = data
} catch (error) {
console.error('Failed to fetch current user:', error)
} finally {
loading.value = false
}
}
// 更新用户信息
const updateUser = async (userData) => {
try {
loading.value = true
const response = await fetch(`/api/user/${userData.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
})
const updatedUser = await response.json()
// 更新本地状态
if (currentUser.value?.id === updatedUser.id) {
currentUser.value = updatedUser
}
const userIndex = users.value.findIndex(u => u.id === updatedUser.id)
if (userIndex !== -1) {
users.value[userIndex] = updatedUser
}
} catch (error) {
console.error('Failed to update user:', error)
} finally {
loading.value = false
}
}
// 计算属性
const userCount = computed(() => users.value.length)
const isLoggedIn = computed(() => !!currentUser.value)
return {
users,
currentUser,
loading,
fetchUsers,
fetchCurrentUser,
updateUser,
userCount,
isLoggedIn
}
}
// 在组件中使用
import { useUserStore } from '@/stores/useUserStore'
export default {
setup() {
const {
users,
currentUser,
loading,
fetchUsers,
fetchCurrentUser,
userCount,
isLoggedIn
} = useUserStore()
// 组件挂载时获取数据
onMounted(() => {
fetchUsers()
fetchCurrentUser()
})
return {
users,
currentUser,
loading,
userCount,
isLoggedIn
}
}
}
多层级状态管理
对于更复杂的应用,我们可以实现多层状态管理:
// stores/index.js
import { reactive } from 'vue'
// 应用级别的全局状态
const appState = reactive({
theme: 'light',
language: 'zh-CN',
notifications: []
})
// 用户相关的状态
const userState = reactive({
profile: null,
permissions: [],
isAuthenticated: false
})
// 数据缓存状态
const cacheState = reactive({
data: new Map(),
lastUpdated: new Date()
})
// 状态管理器
export const useAppStore = () => {
// 应用主题相关操作
const setTheme = (theme) => {
appState.theme = theme
}
const toggleTheme = () => {
appState.theme = appState.theme === 'light' ? 'dark' : 'light'
}
// 用户状态相关操作
const setUserProfile = (profile) => {
userState.profile = profile
userState.isAuthenticated = !!profile
}
const setPermissions = (permissions) => {
userState.permissions = permissions
}
// 缓存相关操作
const cacheData = (key, data) => {
cacheState.data.set(key, data)
cacheState.lastUpdated = new Date()
}
const getCachedData = (key) => {
return cacheState.data.get(key)
}
const clearCache = () => {
cacheState.data.clear()
cacheState.lastUpdated = new Date()
}
return {
// 应用状态
theme: appState.theme,
language: appState.language,
notifications: appState.notifications,
// 用户状态
profile: userState.profile,
permissions: userState.permissions,
isAuthenticated: userState.isAuthenticated,
// 缓存状态
lastUpdated: cacheState.lastUpdated,
// 方法
setTheme,
toggleTheme,
setUserProfile,
setPermissions,
cacheData,
getCachedData,
clearCache
}
}
高级模式和技巧
条件逻辑的处理
在复杂的业务场景中,我们经常需要根据条件动态地应用不同的逻辑:
// composables/useConditionalLogic.js
import { ref, computed } from 'vue'
export function useConditionalLogic(condition) {
const isActive = ref(false)
// 根据条件动态创建逻辑
const conditionalLogic = computed(() => {
if (condition.value === 'featureA') {
return {
name: 'Feature A',
description: 'This is feature A logic',
enabled: true
}
} else if (condition.value === 'featureB') {
return {
name: 'Feature B',
description: 'This is feature B logic',
enabled: false
}
} else {
return {
name: 'Default',
description: 'Default logic',
enabled: true
}
}
})
const toggleActive = () => {
isActive.value = !isActive.value
}
return {
isActive,
conditionalLogic,
toggleActive
}
}
// 在组件中使用
import { useConditionalLogic } from '@/composables/useConditionalLogic'
export default {
setup() {
const condition = ref('featureA')
const { isActive, conditionalLogic, toggleActive } = useConditionalLogic(condition)
return {
condition,
isActive,
conditionalLogic,
toggleActive
}
}
}
异步操作和错误处理
在实际开发中,异步操作的处理是非常重要的:
// composables/useAsyncOperation.js
import { ref, computed } from 'vue'
export function useAsyncOperation() {
const loading = ref(false)
const error = ref(null)
const data = ref(null)
// 异步操作包装器
const executeAsync = async (asyncFunction, ...args) => {
try {
loading.value = true
error.value = null
const result = await asyncFunction(...args)
data.value = result
return result
} catch (err) {
error.value = err
throw err
} finally {
loading.value = false
}
}
// 重试机制
const retryAsync = async (asyncFunction, maxRetries = 3, ...args) => {
let lastError
for (let i = 0; i < maxRetries; i++) {
try {
return await executeAsync(asyncFunction, ...args)
} catch (err) {
lastError = err
// 等待一段时间后重试
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
}
}
throw lastError
}
const clear = () => {
data.value = null
error.value = null
loading.value = false
}
return {
loading,
error,
data,
executeAsync,
retryAsync,
clear
}
}
// 在组件中使用
import { useAsyncOperation } from '@/composables/useAsyncOperation'
export default {
setup() {
const { loading, error, data, executeAsync, retryAsync } = useAsyncOperation()
const fetchData = async () => {
try {
await executeAsync(fetchDataFromApi, 'some-param')
} catch (err) {
console.error('Failed to fetch data:', err)
}
}
const handleRetry = async () => {
try {
await retryAsync(fetchDataFromApi, 3, 'some-param')
} catch (err) {
console.error('Max retries exceeded:', err)
}
}
return {
loading,
error,
data,
fetchData,
handleRetry
}
}
}
性能优化技巧
在使用 Composition API 时,性能优化同样重要:
// composables/useMemoized.js
import { ref, computed } from 'vue'
export function useMemoized(computation, dependencies) {
const cache = ref(null)
const lastDependencies = ref([])
const result = computed(() => {
// 检查依赖是否发生变化
const depsChanged = dependencies.some((dep, index) =>
dep !== lastDependencies.value[index]
)
if (depsChanged || !cache.value) {
cache.value = computation()
lastDependencies.value = [...dependencies]
}
return cache.value
})
return result
}
// 更高级的缓存机制
export function useCache() {
const cache = new Map()
const get = (key) => {
return cache.get(key)
}
const set = (key, value) => {
cache.set(key, value)
}
const has = (key) => {
return cache.has(key)
}
const clear = () => {
cache.clear()
}
return {
get,
set,
has,
clear
}
}
实际应用场景
表单处理组件
让我们创建一个完整的表单处理组件示例:
// composables/useForm.js
import { ref, computed } from 'vue'
export function useForm(initialData = {}) {
const formData = ref({ ...initialData })
const errors = ref({})
const isSubmitting = ref(false)
const isValidating = ref(false)
// 验证规则
const validationRules = ref({})
// 设置验证规则
const setRules = (rules) => {
validationRules.value = rules
}
// 验证单个字段
const validateField = (fieldName) => {
const value = formData.value[fieldName]
const rules = validationRules.value[fieldName]
if (!rules) return true
for (const rule of rules) {
if (rule.required && !value) {
errors.value[fieldName] = rule.message || `${fieldName} is required`
return false
}
if (rule.minLength && value.length < rule.minLength) {
errors.value[fieldName] = rule.message || `${fieldName} must be at least ${rule.minLength} characters`
return false
}
// 更多验证规则...
}
delete errors.value[fieldName]
return true
}
// 验证所有字段
const validateAll = () => {
const fields = Object.keys(validationRules.value)
let isValid = true
fields.forEach(field => {
if (!validateField(field)) {
isValid = false
}
})
return isValid
}
// 更新表单数据
const updateField = (fieldName, value) => {
formData.value[fieldName] = value
delete errors.value[fieldName]
}
// 提交表单
const submit = async (submitFunction) => {
if (!validateAll()) return false
try {
isSubmitting.value = true
const result = await submitFunction(formData.value)
return result
} catch (error) {
console.error('Form submission error:', error)
return false
} finally {
isSubmitting.value = false
}
}
// 重置表单
const reset = () => {
formData.value = { ...initialData }
errors.value = {}
}
// 计算属性
const isValid = computed(() => {
return Object.keys(errors.value).length === 0 && !isSubmitting.value
})
return {
formData,
errors,
isSubmitting,
isValidating,
setRules,
validateField,
validateAll,
updateField,
submit,
reset,
isValid
}
}
// 使用示例组件
import { useForm } from '@/composables/useForm'
export default {
setup() {
const {
formData,
errors,
isSubmitting,
setRules,
updateField,
submit
} = useForm({
name: '',
email: '',
password: ''
})
// 设置验证规则
setRules({
name: [
{ required: true, message: 'Name is required' },
{ minLength: 2, message: 'Name must be at least 2 characters' }
],
email: [
{ required: true, message: 'Email is required' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email format' }
],
password: [
{ required: true, message: 'Password is required' },
{ minLength: 6, message: 'Password must be at least 6 characters' }
]
})
const handleSubmit = async (e) => {
e.preventDefault()
const result = await submit(async (data) => {
// 模拟 API 调用
return new Promise(resolve => {
setTimeout(() => resolve({ success: true, data }), 1000)
})
})
if (result?.success) {
console.log('Form submitted successfully')
}
}
return {
formData,
errors,
isSubmitting,
updateField,
handleSubmit
}
}
}
数据表格组件
另一个实用的场景是创建可复用的数据表格组件:
// composables/useDataTable.js
import { ref, computed, watch } from 'vue'
export function useDataTable(data = [], options = {}) {
const currentPage = ref(1)
const pageSize = ref(options.pageSize || 10)
const sortBy = ref(options.sortBy || null)
const sortDirection = ref(options.sortDirection || 'asc')
const searchQuery = ref('')
// 过滤数据
const filteredData = computed(() => {
if (!searchQuery.value) return data
return data.filter(item => {
return Object.values(item).some(value =>
value.toString().toLowerCase().includes(searchQuery.value.toLowerCase())
)
})
})
// 排序数据
const sortedData = computed(() => {
if (!sortBy.value) return filteredData.value
return [...filteredData.value].sort((a, b) => {
const aValue = a[sortBy.value]
const bValue = b[sortBy.value]
if (aValue < bValue) return sortDirection.value === 'asc' ? -1 : 1
if (aValue > bValue) return sortDirection.value === 'asc' ? 1 : -1
return 0
})
})
// 分页数据
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return sortedData.value.slice(start, end)
})
// 总页数
const totalPages = computed(() => {
return Math.ceil(sortedData.value.length / pageSize.value)
})
// 总记录数
const totalRecords = computed(() => {
return sortedData.value.length
})
// 排序方法
const sort = (field) => {
if (sortBy.value === field) {
sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'
} else {
sortBy.value = field
sortDirection.value = 'asc'
}
}
// 分页方法
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
}
}
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++
}
}
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--
}
}
// 搜索方法
const search = (query) => {
searchQuery.value = query
currentPage.value = 1
}
// 重置分页
const resetPagination = () => {
currentPage.value = 1
searchQuery.value = ''
}
return {
currentPage,
pageSize,
sortBy,
sortDirection,
searchQuery,
filteredData,
sortedData,
paginatedData,
totalPages,
totalRecords,
sort,
goToPage,
nextPage,
prevPage,
search,
resetPagination
}
}
// 使用示例
import { useDataTable } from '@/composables/useDataTable'
export default {
setup() {
const tableData = ref([
{ id: 1, name: 'John', email: 'john@example.com', age: 25 },
{ id: 2, name: 'Jane', email: 'jane@example.com', age: 30 },
{ id: 3, name: 'Bob', email: 'bob@example.com', age: 35 }
])
const {
paginatedData,
totalPages,
currentPage,
goToPage,
sort,
search
} = useDataTable(tableData.value, {
pageSize: 5,
sortBy: 'name',
sortDirection: 'asc'
})
return {
tableData,
paginatedData,
totalPages,
currentPage,
goToPage,
sort,
search
}
}
}
总结
Vue 3 Composition API 为我们提供了一种更加灵活和强大的组件开发方式。通过合理使用 ref、reactive、computed 和 watch 等响应式API,我们可以更好地组织代码逻辑,提高组件的可复用性和可维护性。
在实际开发中,组合式函数是实现逻辑复用的核心工具,它让我们能够将通用的业务逻辑封装成独立的可复用单元。同时,通过合理的状态管理实践,我们可以在应用的不同层级之间有效地共享和管理状态。
性能优化同样重要,我们需要关注响应式数据的使用方式、计算属性的缓存机制以及异步操作的处理策略。通过这些最佳实践,我们可以构建出

评论 (0)