引言
Vue 3 的发布带来了全新的 Composition API,这不仅是对 Vue 2.x Options API 的补充,更是一次重大的架构革新。Composition API 通过将逻辑代码按功能组织,让开发者能够更好地复用和维护复杂的业务逻辑。本文将深入探讨如何使用 Vue 3 Composition API 构建响应式状态管理与组件通信系统,通过实际项目案例展示现代 Vue 应用的构建方法。
Vue 3 Composition API 核心概念
什么是 Composition API
Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式。它允许我们将组件中的逻辑按照功能模块进行组织,而不是传统的按选项分类(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++
}
},
watch: {
count(newVal, oldVal) {
console.log(`count changed from ${oldVal} to ${newVal}`)
}
}
}
// 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++
}
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
return {
count,
name,
reversedName,
increment
}
}
}
响应式系统的核心特性
Vue 3 的响应式系统基于 ES6 的 Proxy 和 Reflect 实现,提供了更强大和灵活的响应式能力:
- ref(): 创建响应式的单值引用
- reactive(): 创建响应式的对象
- computed(): 创建计算属性
- watch(): 监听响应式数据变化
响应式状态管理实践
基础响应式数据操作
在 Composition API 中,我们主要使用 ref 和 reactive 来创建响应式数据:
import { ref, reactive } from 'vue'
export default {
setup() {
// 创建基本类型响应式数据
const count = ref(0)
const message = ref('Hello Vue 3')
// 创建对象响应式数据
const user = reactive({
name: 'John',
age: 25,
email: 'john@example.com'
})
// 修改数据
const increment = () => {
count.value++
}
const updateUserInfo = () => {
user.name = 'Jane'
user.age = 30
}
return {
count,
message,
user,
increment,
updateUserInfo
}
}
}
复杂对象的响应式处理
对于嵌套对象和数组,Vue 3 提供了完整的响应式支持:
import { ref, reactive } from 'vue'
export default {
setup() {
// 嵌套对象响应式
const userInfo = reactive({
profile: {
name: 'John',
age: 25,
address: {
city: 'Beijing',
country: 'China'
}
},
hobbies: ['reading', 'swimming', 'coding']
})
// 数组响应式操作
const addHobby = (hobby) => {
userInfo.hobbies.push(hobby)
}
const removeHobby = (index) => {
userInfo.hobbies.splice(index, 1)
}
// 嵌套对象更新
const updateAddress = (newCity) => {
userInfo.profile.address.city = newCity
}
return {
userInfo,
addHobby,
removeHobby,
updateAddress
}
}
}
自定义响应式组合函数
通过创建自定义的组合函数,我们可以将复杂的逻辑封装起来,实现更好的复用:
// 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
}
}
// composables/useLocalStorage.js
import { ref, watch } from 'vue'
export function useLocalStorage(key, defaultValue) {
const value = ref(defaultValue)
// 从 localStorage 初始化值
const storedValue = localStorage.getItem(key)
if (storedValue) {
try {
value.value = JSON.parse(storedValue)
} catch (error) {
console.error('Failed to parse localStorage value:', error)
}
}
// 监听变化并同步到 localStorage
watch(value, (newValue) => {
try {
localStorage.setItem(key, JSON.stringify(newValue))
} catch (error) {
console.error('Failed to save to localStorage:', error)
}
}, { deep: true })
return value
}
组件间通信机制
Props 和 Emit 通信模式
在 Composition API 中,props 和 emit 的使用方式与 Options API 基本一致:
// Parent.vue
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
setup() {
const message = ref('Hello from parent')
const user = ref({ name: 'John', age: 25 })
const handleChildEvent = (data) => {
console.log('Received from child:', data)
}
return {
message,
user,
handleChildEvent
}
}
}
<!-- Parent.vue -->
<template>
<div>
<child-component
:message="message"
:user="user"
@child-event="handleChildEvent"
/>
</div>
</template>
// ChildComponent.vue
export default {
props: {
message: {
type: String,
required: true
},
user: {
type: Object,
required: true
}
},
setup(props, { emit }) {
const handleClick = () => {
emit('child-event', {
message: 'Hello from child',
timestamp: Date.now()
})
}
return {
handleClick
}
}
}
Provide / Inject 依赖注入
Provide / Inject 是 Vue 3 中强大的跨层级组件通信机制:
// Parent.vue
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
setup() {
const theme = ref('dark')
const user = ref({ name: 'John', role: 'admin' })
// 提供数据给子组件
provide('theme', theme)
provide('user', user)
const toggleTheme = () => {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
return {
toggleTheme
}
}
}
// ChildComponent.vue
import { inject } from 'vue'
export default {
setup() {
// 注入提供的数据
const theme = inject('theme')
const user = inject('user')
const updateUserInfo = () => {
user.value.name = 'Jane'
}
return {
theme,
user,
updateUserInfo
}
}
}
全局状态管理
结合 Composition API 和响应式系统,我们可以构建轻量级的全局状态管理系统:
// store/index.js
import { reactive, readonly } from 'vue'
// 创建全局状态
const state = reactive({
user: null,
isLoggedIn: false,
theme: 'light',
notifications: []
})
// 创建状态操作方法
const setUser = (user) => {
state.user = user
state.isLoggedIn = !!user
}
const setTheme = (theme) => {
state.theme = theme
}
const addNotification = (notification) => {
state.notifications.push({
id: Date.now(),
...notification,
timestamp: new Date()
})
}
const removeNotification = (id) => {
const index = state.notifications.findIndex(n => n.id === id)
if (index > -1) {
state.notifications.splice(index, 1)
}
}
// 创建只读状态访问器
const getters = {
getUser: () => state.user,
getIsLoggedIn: () => state.isLoggedIn,
getTheme: () => state.theme,
getNotifications: () => state.notifications
}
export default {
state: readonly(state),
setUser,
setTheme,
addNotification,
removeNotification,
...getters
}
// composables/useGlobalStore.js
import store from '@/store'
export function useGlobalStore() {
const {
state,
setUser,
setTheme,
addNotification,
removeNotification
} = store
return {
// 状态访问
user: state.user,
isLoggedIn: state.isLoggedIn,
theme: state.theme,
notifications: state.notifications,
// 状态操作
setUser,
setTheme,
addNotification,
removeNotification
}
}
高级响应式编程技巧
响应式数据的深度监听
Vue 3 的响应式系统支持深层监听,这对于复杂的嵌套对象非常有用:
import { ref, watch } from 'vue'
export default {
setup() {
const complexData = ref({
users: [
{ id: 1, name: 'John', profile: { age: 25, city: 'Beijing' } },
{ id: 2, name: 'Jane', profile: { age: 30, city: 'Shanghai' } }
],
settings: {
theme: 'dark',
language: 'zh-CN'
}
})
// 深度监听整个对象
watch(complexData, (newVal, oldVal) => {
console.log('Complex data changed:', newVal)
}, { deep: true })
// 监听特定属性
watch(() => complexData.value.users, (newUsers, oldUsers) => {
console.log('Users array changed:', newUsers)
}, { deep: true })
// 监听嵌套属性
watch(() => complexData.value.settings.theme, (newTheme, oldTheme) => {
console.log(`Theme changed from ${oldTheme} to ${newTheme}`)
})
return {
complexData
}
}
}
计算属性的优化
计算属性在 Composition API 中提供了更好的性能和灵活性:
import { ref, computed } from 'vue'
export default {
setup() {
const items = ref([])
const filterText = ref('')
// 基础计算属性
const filteredItems = computed(() => {
return items.value.filter(item =>
item.name.toLowerCase().includes(filterText.value.toLowerCase())
)
})
// 带有 getter 和 setter 的计算属性
const fullName = computed({
get: () => {
return `${firstName.value} ${lastName.value}`
},
set: (value) => {
const names = value.split(' ')
firstName.value = names[0]
lastName.value = names[1] || ''
}
})
// 高性能计算属性
const expensiveComputed = computed(() => {
// 模拟耗时操作
let result = 0
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i)
}
return result
})
return {
items,
filterText,
filteredItems,
fullName,
expensiveComputed
}
}
}
异步数据处理
在实际应用中,经常需要处理异步数据加载和状态管理:
import { ref, reactive, onMounted } from 'vue'
export default {
setup() {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
// 模拟 API 调用
const fetchData = async () => {
try {
loading.value = true
error.value = null
// 模拟网络请求延迟
await new Promise(resolve => setTimeout(resolve, 1000))
data.value = {
id: 1,
name: 'Sample Data',
items: ['item1', 'item2', 'item3']
}
} catch (err) {
error.value = err.message
console.error('Failed to fetch data:', err)
} finally {
loading.value = false
}
}
// 在组件挂载时获取数据
onMounted(() => {
fetchData()
})
return {
data,
loading,
error,
fetchData
}
}
}
实际项目案例:购物车应用
让我们通过一个完整的购物车应用来展示如何使用 Composition API 构建响应式状态管理:
// composables/useCart.js
import { ref, computed } from 'vue'
export function useCart() {
const items = ref([])
// 添加商品到购物车
const addItem = (product) => {
const existingItem = items.value.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity += 1
} else {
items.value.push({
...product,
quantity: 1
})
}
}
// 移除商品
const removeItem = (productId) => {
const index = items.value.findIndex(item => item.id === productId)
if (index > -1) {
items.value.splice(index, 1)
}
}
// 更新商品数量
const updateQuantity = (productId, quantity) => {
const item = items.value.find(item => item.id === productId)
if (item) {
item.quantity = Math.max(0, quantity)
if (item.quantity === 0) {
removeItem(productId)
}
}
}
// 计算总价
const totalPrice = computed(() => {
return items.value.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0)
})
// 计算商品总数
const totalItems = computed(() => {
return items.value.reduce((total, item) => total + item.quantity, 0)
})
// 清空购物车
const clearCart = () => {
items.value = []
}
return {
items,
addItem,
removeItem,
updateQuantity,
totalPrice,
totalItems,
clearCart
}
}
// components/Cart.vue
<template>
<div class="cart">
<h2>购物车</h2>
<div v-if="totalItems === 0" class="empty-cart">
购物车为空
</div>
<div v-else>
<div
v-for="item in items"
:key="item.id"
class="cart-item"
>
<div class="item-info">
<h3>{{ item.name }}</h3>
<p>价格: ¥{{ item.price }}</p>
</div>
<div class="item-controls">
<button @click="updateQuantity(item.id, item.quantity - 1)">-</button>
<span class="quantity">{{ item.quantity }}</span>
<button @click="updateQuantity(item.id, item.quantity + 1)">+</button>
<button @click="removeItem(item.id)" class="remove-btn">删除</button>
</div>
</div>
<div class="cart-summary">
<p>商品总数: {{ totalItems }}</p>
<p>总价: ¥{{ totalPrice.toFixed(2) }}</p>
<button @click="clearCart" class="clear-btn">清空购物车</button>
</div>
</div>
</div>
</template>
<script>
import { useCart } from '@/composables/useCart'
export default {
name: 'Cart',
setup() {
const {
items,
addItem,
removeItem,
updateQuantity,
totalPrice,
totalItems,
clearCart
} = useCart()
return {
items,
addItem,
removeItem,
updateQuantity,
totalPrice,
totalItems,
clearCart
}
}
}
</script>
<style scoped>
.cart {
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.empty-cart {
text-align: center;
color: #999;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.item-controls {
display: flex;
align-items: center;
gap: 10px;
}
.quantity {
min-width: 30px;
text-align: center;
}
.remove-btn, .clear-btn {
background-color: #ff4757;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
}
.clear-btn {
background-color: #ff6b81;
}
.cart-summary {
margin-top: 20px;
padding-top: 20px;
border-top: 2px solid #eee;
}
</style>
最佳实践与性能优化
组合函数的设计原则
良好的组合函数应该具备以下特点:
// ✅ 好的组合函数设计
export function useApi(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const fetch = async () => {
try {
loading.value = true
error.value = null
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
data.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
// 可以选择性地暴露一些方法
const refresh = () => fetch()
return {
data,
loading,
error,
fetch,
refresh
}
}
// ❌ 不好的设计
export function useApi(url) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
// 过多的暴露方法
const fetch = async () => { /* ... */ }
const getData = () => data.value
const setLoading = (value) => loading.value = value
const setError = (value) => error.value = value
return {
data,
loading,
error,
fetch,
getData,
setLoading,
setError
}
}
性能优化策略
- 避免不必要的计算: 使用
computed来缓存昂贵的计算结果 - 合理使用 watch: 避免监听不需要的数据
- 组件级别的优化: 使用
defineAsyncComponent进行懒加载
import { ref, computed, watch, defineAsyncComponent } from 'vue'
export default {
setup() {
const data = ref([])
// 优化的计算属性
const filteredData = computed(() => {
// 只在依赖变化时重新计算
return data.value.filter(item => item.visible)
})
// 避免在渲染过程中执行昂贵操作
const expensiveOperation = computed(() => {
// 这个计算只会在需要时执行
return data.value.reduce((acc, item) => acc + item.value, 0)
})
// 智能监听
watch(data, (newData) => {
// 只在数据真正改变时执行
console.log('Data changed:', newData.length)
}, { deep: true })
// 异步组件懒加载
const AsyncComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
return {
data,
filteredData,
expensiveOperation,
AsyncComponent
}
}
}
总结
Vue 3 Composition API 为前端开发带来了更灵活、更强大的状态管理和组件通信能力。通过本文的介绍,我们了解了:
- 响应式数据管理: 使用
ref、reactive等 API 创建和操作响应式数据 - 组件通信机制: Props、Emit、Provide/Inject 等多种通信方式的应用
- 组合函数设计: 如何封装可复用的逻辑代码
- 实际项目应用: 通过购物车应用展示了完整的开发流程
- 最佳实践: 性能优化和设计原则
Composition API 的核心优势在于它让我们能够更自由地组织代码逻辑,将相关的功能组合在一起,而不是被传统的选项分类所限制。这使得大型应用的维护变得更加容易,也提高了代码的可读性和复用性。
在实际开发中,建议根据项目需求选择合适的模式:
- 简单组件可以使用 Options API
- 复杂逻辑推荐使用 Composition API
- 可以混合使用两种 API 来发挥各自优势
通过持续实践和优化,Vue 3 Composition API 将帮助我们构建更加现代化、高效的前端应用。

评论 (0)