引言
Vue 3的发布带来了革命性的变化,其中最引人注目的就是Composition API的引入。相比于Vue 2的选项式API,Composition API为开发者提供了更加灵活和强大的组件开发方式。在实际项目开发中,组件通信和状态管理是两个核心问题,而Composition API恰好为我们提供了优雅的解决方案。
本文将深入探讨Vue 3 Composition API的核心特性,详细演示组件间通信、响应式数据管理、组合式函数封装等高级用法,并提供Vue 3项目开发的最佳实践指导。通过实际代码示例和详细的技术分析,帮助开发者更好地理解和应用这些高级特性。
Vue 3 Composition API核心特性
什么是Composition API
Composition API是Vue 3中引入的一种新的组件开发方式,它允许我们使用函数来组织和复用组件逻辑。与Vue 2的选项式API不同,Composition API将组件的逻辑按照功能进行组织,而不是按照选项(data、methods、computed等)进行划分。
主要优势
- 更好的逻辑复用:通过组合式函数(Composable Functions)可以轻松地在组件间共享和复用逻辑
- 更灵活的代码组织:可以根据功能而非选项来组织代码
- 更好的类型推断:与TypeScript集成更佳
- 更清晰的代码结构:避免了Vue 2中复杂的this指向问题
核心API函数
// Vue 3 Composition API核心函数
import {
ref,
reactive,
computed,
watch,
watchEffect,
onMounted,
onUpdated,
onUnmounted,
provide,
inject
} from 'vue'
响应式数据管理
Ref与Reactive的区别
在Vue 3中,响应式数据管理主要通过ref和reactive两个函数来实现:
import { ref, reactive } from 'vue'
// 使用ref创建响应式数据
const count = ref(0)
const message = ref('Hello Vue 3')
// 使用reactive创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue 3',
user: {
name: 'John',
age: 25
}
})
// 访问和修改数据
console.log(count.value) // 0
count.value = 10
console.log(state.count) // 0
state.count = 10
深层响应式与浅层响应式
import { ref, reactive, shallowRef, shallowReactive } from 'vue'
// 深层响应式
const deepState = reactive({
user: {
profile: {
name: 'John'
}
}
})
// 浅层响应式
const shallowState = shallowReactive({
user: {
profile: {
name: 'John'
}
}
})
// 浅层ref
const shallowRefValue = shallowRef({
name: 'John'
})
计算属性与监听器
import { ref, computed, watch, watchEffect } 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], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)
})
// watchEffect
watchEffect(() => {
console.log(`Current count: ${count.value}`)
console.log(`Full name: ${fullName.value}`)
})
组件间通信实战
Props通信
在Composition API中,props的使用方式与Vue 2略有不同:
// 子组件
import { defineProps, computed } from 'vue'
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
},
user: {
type: Object,
default: () => ({})
}
})
// 使用props
const displayTitle = computed(() => {
return props.title.toUpperCase()
})
const isCountPositive = computed(() => {
return props.count > 0
})
emit事件通信
// 子组件
import { defineEmits } from 'vue'
const emit = defineEmits(['update:count', 'user-selected', 'delete-item'])
// 触发事件
const handleIncrement = () => {
emit('update:count', props.count + 1)
}
const handleUserSelect = (user) => {
emit('user-selected', user)
}
const handleDelete = (id) => {
emit('delete-item', id)
}
父子组件通信示例
<!-- 父组件 -->
<template>
<div>
<h2>父组件</h2>
<counter
:count="counterValue"
:title="counterTitle"
@update:count="handleCountUpdate"
/>
<user-list
:users="users"
@user-selected="handleUserSelect"
/>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import Counter from './Counter.vue'
import UserList from './UserList.vue'
const counterValue = ref(0)
const counterTitle = ref('计数器')
const users = reactive([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
])
const handleCountUpdate = (newCount) => {
counterValue.value = newCount
}
const handleUserSelect = (user) => {
console.log('Selected user:', user)
}
</script>
<!-- 子组件 Counter.vue -->
<template>
<div class="counter">
<h3>{{ displayTitle }}</h3>
<p>当前计数: {{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits, computed } from 'vue'
const props = defineProps({
count: {
type: Number,
default: 0
},
title: {
type: String,
required: true
}
})
const emit = defineEmits(['update:count'])
const displayTitle = computed(() => {
return props.title.toUpperCase()
})
const increment = () => {
emit('update:count', props.count + 1)
}
const decrement = () => {
emit('update:count', props.count - 1)
}
</script>
状态管理最佳实践
简单状态管理
对于小型应用,可以使用简单的响应式状态管理:
// store.js
import { reactive, readonly } from 'vue'
// 创建状态
const state = reactive({
user: null,
isLoggedIn: false,
theme: 'light'
})
// 创建actions
const setUser = (user) => {
state.user = user
state.isLoggedIn = !!user
}
const setTheme = (theme) => {
state.theme = theme
}
const logout = () => {
state.user = null
state.isLoggedIn = false
}
// 导出只读状态和方法
export const useStore = () => {
return {
state: readonly(state),
setUser,
setTheme,
logout
}
}
复杂状态管理
对于更复杂的应用,可以创建更完善的组合式函数:
// composables/useUserStore.js
import { reactive, readonly, computed } from 'vue'
export const useUserStore = () => {
// 状态
const state = reactive({
users: [],
currentUser: null,
loading: false,
error: null
})
// 计算属性
const isLoggedIn = computed(() => !!state.currentUser)
const userCount = computed(() => state.users.length)
// actions
const fetchUsers = async () => {
state.loading = true
try {
const response = await fetch('/api/users')
const users = await response.json()
state.users = users
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
}
const setCurrentUser = (user) => {
state.currentUser = user
}
const updateUser = (updatedUser) => {
const index = state.users.findIndex(u => u.id === updatedUser.id)
if (index !== -1) {
state.users[index] = updatedUser
}
if (state.currentUser?.id === updatedUser.id) {
state.currentUser = updatedUser
}
}
const deleteUser = (userId) => {
state.users = state.users.filter(u => u.id !== userId)
if (state.currentUser?.id === userId) {
state.currentUser = null
}
}
// 返回只读状态和方法
return {
state: readonly(state),
isLoggedIn,
userCount,
fetchUsers,
setCurrentUser,
updateUser,
deleteUser
}
}
全局状态管理示例
<!-- App.vue -->
<template>
<div :class="['app', theme]">
<header>
<h1>我的应用</h1>
<nav>
<button @click="toggleTheme">切换主题</button>
<button @click="logout" v-if="isLoggedIn">退出登录</button>
<button @click="login" v-else>登录</button>
</nav>
</header>
<main>
<router-view />
</main>
</div>
</template>
<script setup>
import { useUserStore } from './composables/useUserStore'
import { useThemeStore } from './composables/useThemeStore'
const { state: userState, logout, setCurrentUser } = useUserStore()
const { state: themeState, toggleTheme } = useThemeStore()
const isLoggedIn = computed(() => userState.isLoggedIn)
const login = async () => {
try {
const response = await fetch('/api/login')
const user = await response.json()
setCurrentUser(user)
} catch (error) {
console.error('Login failed:', error)
}
}
</script>
<style>
.app {
min-height: 100vh;
transition: background-color 0.3s, color 0.3s;
}
.app.dark {
background-color: #1a1a1a;
color: #fff;
}
.app.light {
background-color: #fff;
color: #000;
}
</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, computed } from 'vue'
export function useApi(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)
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
} finally {
loading.value = false
}
}
const refresh = () => fetchData()
const isLoading = computed(() => loading.value)
const hasError = computed(() => !!error.value)
return {
data,
loading: isLoading,
error,
refresh,
fetchData
}
}
实际应用示例
<!-- UserList.vue -->
<template>
<div class="user-list">
<div class="controls">
<button @click="fetchUsers" :disabled="loading">
{{ loading ? '加载中...' : '刷新用户列表' }}
</button>
<button @click="toggleShowInactive">
{{ showInactive ? '隐藏非活跃用户' : '显示所有用户' }}
</button>
</div>
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="hasError" class="error">{{ error }}</div>
<div v-else class="users">
<user-card
v-for="user in filteredUsers"
:key="user.id"
:user="user"
@delete="handleDelete"
/>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useApi } from '../composables/useApi'
import { useLocalStorage } from '../composables/useLocalStorage'
import UserCard from './UserCard.vue'
const { data, loading, error, refresh, fetchData } = useApi('/api/users')
const showInactive = useLocalStorage('showInactiveUsers', false)
const filteredUsers = computed(() => {
if (!data.value) return []
return showInactive.value
? data.value
: data.value.filter(user => user.isActive)
})
const fetchUsers = async () => {
await fetchData()
}
const handleDelete = async (userId) => {
try {
await fetch(`/api/users/${userId}`, { method: 'DELETE' })
await refresh() // 重新获取数据
} catch (err) {
console.error('Delete failed:', err)
}
}
const toggleShowInactive = () => {
showInactive.value = !showInactive.value
}
onMounted(() => {
fetchUsers()
})
</script>
高级通信模式
provide/inject模式
// 父组件
import { provide } from 'vue'
import { useUserStore } from '../composables/useUserStore'
export default {
setup() {
const { state, logout } = useUserStore()
provide('userStore', {
state,
logout
})
return {
state
}
}
}
<!-- 子组件 -->
<template>
<div class="user-info">
<p>欢迎, {{ userStore.state.currentUser?.name }}</p>
<button @click="userStore.logout">退出登录</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
const userStore = inject('userStore')
</script>
事件总线模式
// eventBus.js
import { createApp } from 'vue'
export const eventBus = createApp({}).config.globalProperties.$bus = {}
// 或者使用更简单的实现
import { reactive } from 'vue'
const events = reactive({})
export const emit = (event, data) => {
if (events[event]) {
events[event].forEach(callback => callback(data))
}
}
export const on = (event, callback) => {
if (!events[event]) {
events[event] = []
}
events[event].push(callback)
}
export const off = (event, callback) => {
if (events[event]) {
events[event] = events[event].filter(cb => cb !== callback)
}
}
性能优化技巧
避免不必要的计算
// 不好的做法
const expensiveValue = computed(() => {
// 复杂的计算逻辑
return someComplexOperation(data.value)
})
// 好的做法 - 使用缓存
import { computed } from 'vue'
const expensiveValue = computed(() => {
// 使用缓存机制
return someComplexOperation(data.value)
})
// 对于需要深度计算的场景
const expensiveValue = computed({
get: () => {
// 计算逻辑
},
set: (value) => {
// 设置逻辑
}
})
合理使用watchEffect
// 避免在watchEffect中进行复杂操作
watchEffect(() => {
// 简单的依赖追踪
console.log('Count changed:', count.value)
})
// 对于复杂操作,使用watch
watch(count, (newVal, oldVal) => {
// 复杂的异步操作
debouncedSave(newVal)
})
最佳实践总结
代码组织原则
- 按功能分组:将相关的逻辑组织在一起
- 可复用性:将通用逻辑封装成组合式函数
- 清晰的命名:使用语义化的函数和变量命名
- 文档化:为组合式函数添加详细的注释
状态管理建议
- 简单应用:使用简单的响应式状态
- 复杂应用:使用组合式函数进行状态管理
- 全局状态:使用provide/inject进行全局状态共享
- 持久化:合理使用localStorage等进行状态持久化
错误处理
// 统一的错误处理
import { ref } from 'vue'
export function useErrorHandler() {
const error = ref(null)
const handleError = (error) => {
console.error('Error occurred:', error)
error.value = error.message
}
const clearError = () => {
error.value = null
}
return {
error,
handleError,
clearError
}
}
结论
Vue 3的Composition API为组件开发带来了革命性的变化,它不仅提供了更加灵活的代码组织方式,还为组件间通信和状态管理提供了强大的解决方案。通过合理使用ref、reactive、computed等响应式API,以及创建可复用的组合式函数,我们可以构建出更加优雅和可维护的Vue应用。
在实际开发中,我们应该根据应用的复杂度选择合适的状态管理方式,从简单的响应式数据到复杂的全局状态管理,都要遵循一致的开发原则和最佳实践。同时,要注意性能优化,避免不必要的计算和监听,确保应用的流畅运行。
随着Vue 3生态的不断完善,Composition API将成为Vue开发的标准方式,掌握这些高级特性将大大提高我们的开发效率和代码质量。希望本文的实践指导能够帮助开发者更好地理解和应用Vue 3 Composition API,在实际项目中发挥其最大价值。

评论 (0)