引言
Vue 3 的发布带来了 Composition API 的重大革新,这一特性不仅改变了我们编写组件的方式,更深刻地影响了前端应用的架构设计模式。Composition API 通过将逻辑组织成可重用的函数,为大型应用提供了更灵活、更可维护的开发体验。本文将深入探讨 Composition API 的架构设计理念,详细阐述组件间通信模式、响应式数据管理、状态持久化等关键技术,并通过实际项目案例演示如何构建可维护、可扩展的现代化前端应用架构。
Vue 3 Composition API 核心概念
什么是 Composition API
Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式。与传统的 Options API 不同,Composition API 允许我们按照逻辑功能来组织代码,而不是按照选项类型(data、methods、computed 等)来分组。这种组织方式使得代码更加灵活,更易于维护和重用。
Composition API 的优势
- 更好的逻辑复用:通过组合函数(composables)实现逻辑复用,避免了 Mixins 的命名空间冲突问题
- 更清晰的代码组织:按照功能逻辑组织代码,而不是按照选项类型
- 更灵活的组件设计:可以更自由地组合和重用逻辑
- 更好的 TypeScript 支持:类型推断更加准确和直观
基础 API 详解
import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue'
export default {
setup() {
// 响应式数据
const count = ref(0)
const user = reactive({
name: 'John',
age: 30
})
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 监听器
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// 生命周期钩子
onMounted(() => {
console.log('Component mounted')
})
// 返回给模板使用的数据和方法
return {
count,
user,
doubleCount,
increment: () => count.value++
}
}
}
组件间通信模式
1. Props 和 Emit 模式
Props 和 Emit 是 Vue 中最基本的组件通信方式,适用于父子组件通信。
// 父组件
<template>
<child-component
:user="currentUser"
@user-updated="handleUserUpdate"
@submit="handleSubmit"
/>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const currentUser = ref({
name: 'John',
email: 'john@example.com'
})
const handleUserUpdate = (updatedUser) => {
currentUser.value = updatedUser
}
const handleSubmit = (formData) => {
console.log('Form submitted:', formData)
}
</script>
// 子组件
<template>
<div class="user-form">
<input v-model="user.name" placeholder="Name" />
<input v-model="user.email" placeholder="Email" />
<button @click="submitForm">Submit</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
user: {
type: Object,
required: true
}
})
const emit = defineEmits(['userUpdated', 'submit'])
const user = ref({ ...props.user })
// 监听 props 变化
watch(() => props.user, (newUser) => {
user.value = { ...newUser }
})
const submitForm = () => {
emit('userUpdated', user.value)
emit('submit', user.value)
}
</script>
2. Provide/Inject 模式
Provide/Inject 模式适用于跨层级组件通信,避免了 props 的层层传递。
// 父组件
<template>
<div class="app">
<child-component />
</div>
</template>
<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const theme = ref('dark')
const user = ref({ name: 'John', role: 'admin' })
provide('appTheme', theme)
provide('currentUser', user)
provide('updateUser', (newUser) => {
user.value = newUser
})
</script>
// 子组件
<template>
<div :class="theme">
<h1>{{ user.name }}</h1>
<button @click="changeTheme">Toggle Theme</button>
</div>
</template>
<script setup>
import { inject, ref } from 'vue'
const theme = inject('appTheme')
const user = inject('currentUser')
const updateUser = inject('updateUser')
const changeTheme = () => {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
</script>
3. 全局状态管理模式
对于复杂的跨组件通信需求,可以使用全局状态管理。
// stores/userStore.js
import { reactive, readonly } from 'vue'
const state = reactive({
currentUser: null,
isLoggedIn: false,
permissions: []
})
const setUser = (user) => {
state.currentUser = user
state.isLoggedIn = !!user
state.permissions = user?.permissions || []
}
const logout = () => {
state.currentUser = null
state.isLoggedIn = false
state.permissions = []
}
const getUser = () => readonly(state.currentUser)
export const useUserStore = () => {
return {
state: readonly(state),
setUser,
logout,
getUser
}
}
// 在组件中使用
<script setup>
import { useUserStore } from '@/stores/userStore'
const { state, setUser, logout } = useUserStore()
const handleLogin = (userData) => {
setUser(userData)
}
const handleLogout = () => {
logout()
}
</script>
响应式数据管理
1. Ref 和 Reactive 的使用
Ref 和 Reactive 是 Vue 3 响应式系统的核心,它们提供了不同的响应式数据处理方式。
import { ref, reactive, toRefs, toRaw } from 'vue'
// Ref 用于基本数据类型
const count = ref(0)
const name = ref('John')
const isActive = ref(true)
// Reactive 用于对象和数组
const user = reactive({
name: 'John',
age: 30,
hobbies: ['reading', 'coding'],
address: {
city: 'New York',
country: 'USA'
}
})
// 使用 toRefs 转换响应式对象
const useUserStore = () => {
const user = reactive({
name: 'John',
age: 30,
email: 'john@example.com'
})
// 将响应式对象转换为 ref
return {
...toRefs(user)
}
}
2. 计算属性和监听器
计算属性和监听器是响应式数据管理的重要组成部分。
import { ref, computed, watch, watchEffect } from 'vue'
export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')
const age = ref(30)
// 计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
const isAdult = computed(() => {
return age.value >= 18
})
// 带 getter 和 setter 的计算属性
const displayName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (value) => {
const names = value.split(' ')
firstName.value = names[0]
lastName.value = names[1]
}
})
// 监听器
watch(firstName, (newVal, oldVal) => {
console.log(`First name changed from ${oldVal} to ${newVal}`)
})
// 监听多个值
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)
})
// watchEffect
const watchEffectExample = () => {
const count = ref(0)
watchEffect(() => {
console.log(`Count is: ${count.value}`)
})
// 会自动追踪依赖
count.value++ // 输出: Count is: 1
}
return {
firstName,
lastName,
age,
fullName,
isAdult,
displayName
}
}
}
3. 深度响应式和浅响应式
Vue 3 提供了不同级别的响应式处理能力。
import { ref, reactive, shallowRef, shallowReactive, readonly } from 'vue'
// 深度响应式
const deepObj = reactive({
nested: {
value: 1
}
})
// 浅响应式 - 只响应顶层属性
const shallowObj = shallowReactive({
nested: {
value: 1
}
})
// 浅 ref - 只响应顶层属性
const shallowRefObj = shallowRef({
nested: {
value: 1
}
})
// 只读响应式
const readOnlyData = readonly({
name: 'John',
age: 30
})
状态持久化策略
1. localStorage 持久化
// 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
}
// 使用示例
<script setup>
import { useLocalStorage } from '@/composables/useLocalStorage'
const userPreferences = useLocalStorage('userPreferences', {
theme: 'light',
language: 'en',
notifications: true
})
const toggleTheme = () => {
userPreferences.value.theme =
userPreferences.value.theme === 'light' ? 'dark' : 'light'
}
</script>
2. SessionStorage 持久化
// composables/useSessionStorage.js
import { ref, watch } from 'vue'
export function useSessionStorage(key, defaultValue) {
const storedValue = sessionStorage.getItem(key)
const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
watch(value, (newValue) => {
sessionStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return value
}
// 用于存储临时状态
const tempData = useSessionStorage('tempData', {})
3. IndexedDB 持久化
对于复杂的数据持久化需求,可以使用 IndexedDB。
// composables/useIndexedDB.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useIndexedDB(dbName, storeName) {
const db = ref(null)
const isReady = ref(false)
const initDB = async () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, 1)
request.onerror = () => reject(request.error)
request.onsuccess = () => {
db.value = request.result
isReady.value = true
resolve(request.result)
}
request.onupgradeneeded = (event) => {
const db = event.target.result
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName, { keyPath: 'id' })
}
}
})
}
const saveData = async (data) => {
if (!db.value || !isReady.value) {
await initDB()
}
const transaction = db.value.transaction([storeName], 'readwrite')
const store = transaction.objectStore(storeName)
const request = store.add({ ...data, id: Date.now() })
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error)
})
}
const getData = async (id) => {
if (!db.value || !isReady.value) {
await initDB()
}
const transaction = db.value.transaction([storeName], 'readonly')
const store = transaction.objectStore(storeName)
const request = store.get(id)
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result)
request.onerror = () => reject(request.error)
})
}
onMounted(() => {
initDB()
})
return {
saveData,
getData,
isReady
}
}
组合函数最佳实践
1. 组合函数命名规范
// 好的命名规范
export function useUserStore() { /* ... */ }
export function useApiRequest() { /* ... */ }
export function useLocalStorage() { /* ... */ }
export function useWindowResize() { /* ... */ }
// 避免的命名
export function userStore() { /* ... */ }
export function api() { /* ... */ }
export function localStorage() { /* ... */ }
2. 组合函数参数设计
// 带默认参数的组合函数
export function useFetch(url, options = {}) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const fetchData = async () => {
loading.value = true
error.value = null
try {
const response = await fetch(url, options)
data.value = await response.json()
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
fetchData
}
}
// 使用示例
const { data, loading, error, fetchData } = useFetch('/api/users')
fetchData()
3. 组合函数返回值设计
// 返回可复用的组合函数
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
}
}
// 使用示例
const { count, increment, decrement } = useCounter(10)
实际项目架构案例
1. 电商应用架构
// composables/useCart.js
import { ref, computed } from 'vue'
import { useLocalStorage } from './useLocalStorage'
export function useCart() {
const items = useLocalStorage('cartItems', [])
const cartCount = computed(() => items.value.length)
const cartTotal = computed(() => {
return items.value.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0)
})
const addToCart = (product) => {
const existingItem = items.value.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity += 1
} else {
items.value.push({
...product,
quantity: 1
})
}
}
const removeFromCart = (productId) => {
items.value = items.value.filter(item => item.id !== productId)
}
const updateQuantity = (productId, quantity) => {
const item = items.value.find(item => item.id === productId)
if (item) {
item.quantity = quantity
}
}
return {
items,
cartCount,
cartTotal,
addToCart,
removeFromCart,
updateQuantity
}
}
// composables/useAuth.js
import { ref, computed } from 'vue'
import { useLocalStorage } from './useLocalStorage'
export function useAuth() {
const user = useLocalStorage('currentUser', null)
const token = useLocalStorage('authToken', null)
const isLoggedIn = computed(() => !!user.value && !!token.value)
const login = (userData, tokenData) => {
user.value = userData
token.value = tokenData
}
const logout = () => {
user.value = null
token.value = null
}
const updateProfile = (profileData) => {
if (user.value) {
user.value = { ...user.value, ...profileData }
}
}
return {
user,
token,
isLoggedIn,
login,
logout,
updateProfile
}
}
2. 数据管理架构
// services/api.js
import { ref } from 'vue'
const API_BASE_URL = 'https://api.example.com'
export class ApiService {
static async get(endpoint) {
const response = await fetch(`${API_BASE_URL}${endpoint}`)
return response.json()
}
static async post(endpoint, data) {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
return response.json()
}
static async put(endpoint, data) {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
return response.json()
}
static async delete(endpoint) {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'DELETE'
})
return response.json()
}
}
// composables/useResource.js
import { ref, computed } from 'vue'
import { ApiService } from '@/services/api'
export function useResource(resourceName, id = null) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const resource = computed(() => data.value)
const fetch = async () => {
loading.value = true
error.value = null
try {
if (id) {
data.value = await ApiService.get(`/${resourceName}/${id}`)
} else {
data.value = await ApiService.get(`/${resourceName}`)
}
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
const create = async (payload) => {
loading.value = true
error.value = null
try {
const result = await ApiService.post(`/${resourceName}`, payload)
data.value = result
return result
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
const update = async (payload) => {
loading.value = true
error.value = null
try {
const result = await ApiService.put(`/${resourceName}/${id}`, payload)
data.value = result
return result
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
const remove = async () => {
loading.value = true
error.value = null
try {
await ApiService.delete(`/${resourceName}/${id}`)
data.value = null
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
resource,
loading,
error,
fetch,
create,
update,
remove
}
}
性能优化策略
1. 计算属性缓存
// 优化前
const expensiveCalculation = computed(() => {
// 复杂计算
return data.value.items.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
})
// 优化后 - 使用缓存
const expensiveCalculation = computed({
get: () => {
// 复杂计算
return data.value.items.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
},
set: (value) => {
// 设置逻辑
}
})
2. 组件懒加载
// 使用动态导入实现懒加载
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
)
// 在模板中使用
<template>
<async-component v-if="showComponent" />
</template>
<script setup>
import { ref } from 'vue'
const showComponent = ref(false)
const toggleComponent = () => {
showComponent.value = !showComponent.value
}
</script>
3. 事件处理优化
// 防抖和节流优化
import { ref } from 'vue'
export function useDebounce(fn, delay = 300) {
let timeoutId
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn.apply(this, args), delay)
}
}
export function useThrottle(fn, limit = 300) {
let inThrottle
return (...args) => {
if (!inThrottle) {
fn.apply(this, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// 使用示例
const debouncedSearch = useDebounce(async (query) => {
// 搜索逻辑
}, 500)
const throttledScroll = useThrottle(() => {
// 滚动处理逻辑
}, 100)
总结
Vue 3 Composition API 为前端应用架构带来了革命性的变化。通过合理的组件通信模式设计、响应式数据管理策略以及状态持久化方案,我们可以构建出更加可维护、可扩展的现代化前端应用。
在实际项目中,我们应当:
- 合理选择通信方式:根据组件层级关系选择合适的通信模式
- 善用组合函数:将可复用的逻辑封装成组合函数
- 注重性能优化:合理使用计算属性缓存、事件处理优化等技术
- 数据持久化策略:根据数据重要性和复杂度选择合适的持久化方案
- 遵循最佳实践:保持代码的一致性和可读性
通过深入理解和灵活运用 Composition API 的设计理念,我们可以构建出更加优雅、高效的前端应用架构,为用户提供更好的体验,同时提升开发效率和代码质量。随着 Vue 生态的不断发展,Composition API 将继续发挥重要作用,推动前端开发技术的进步。

评论 (0)