前言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。相比 Vue 2 中的 Options API,Composition API 提供了更加灵活和强大的组件状态管理方式。本文将深入探讨 Composition API 的核心概念、使用方法以及在实际项目中的最佳实践。
什么是 Composition API
Composition API 是 Vue 3 中引入的一种新的组件状态管理方式,它允许开发者以函数的形式组织和重用组件逻辑。与传统的 Options API 相比,Composition API 更加灵活,能够更好地处理复杂的状态逻辑,特别是在需要在多个组件间共享逻辑时。
Composition API 的核心优势
- 更好的逻辑复用:通过组合函数实现逻辑的复用
- 更清晰的代码组织:将相关的逻辑集中在一起
- 更强的类型支持:与 TypeScript 集成更好
- 更灵活的状态管理:可以动态地添加和移除状态
响应式 API 基础
reactive 和 ref 的基本使用
在 Composition API 中,响应式数据主要通过 reactive 和 ref 两个核心函数来创建。
import { reactive, ref } from 'vue'
// 使用 ref 创建响应式数据
const count = ref(0)
const name = ref('Vue')
// 使用 reactive 创建响应式对象
const state = reactive({
count: 0,
name: 'Vue',
user: {
age: 20,
email: 'vue@example.com'
}
})
// 在模板中使用
console.log(count.value) // 0
console.log(state.count) // 0
// 修改值
count.value = 10
state.count = 5
ref 与 reactive 的区别
import { ref, reactive } from 'vue'
// ref 创建的是响应式引用
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
// reactive 创建的是响应式对象
const state = reactive({
count: 0,
doubleCount: computed(() => state.count * 2)
})
// 在模板中使用时的区别
// ref 需要 .value 访问
// reactive 直接访问属性
响应式数据的解构和展开
import { reactive, toRefs } from 'vue'
const state = reactive({
count: 0,
name: 'Vue',
age: 20
})
// 使用 toRefs 解构响应式对象
const { count, name, age } = toRefs(state)
// 这样解构后的变量仍然是响应式的
count.value++ // 状态会更新
计算属性和监听器
computed 的使用
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// 基本计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 带有 getter 和 setter 的计算属性
const reversedName = computed({
get: () => {
return firstName.value.split('').reverse().join('')
},
set: (value) => {
firstName.value = value.split('').reverse().join('')
}
})
watch 的使用
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Vue')
// 基本监听器
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})
// 监听多个源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
})
// watchEffect 会自动追踪依赖
watchEffect(() => {
console.log(`当前 count 值为: ${count.value}`)
})
// 停止监听器
const stop = watch(count, (newVal) => {
console.log(newVal)
})
stop() // 停止监听
生命周期钩子
Composition API 中的生命周期钩子
import { onMounted, onUpdated, onUnmounted, onBeforeMount } from 'vue'
export default {
setup() {
onBeforeMount(() => {
console.log('组件即将挂载')
})
onMounted(() => {
console.log('组件已挂载')
})
onUpdated(() => {
console.log('组件已更新')
})
onUnmounted(() => {
console.log('组件即将卸载')
})
return {
// 返回的数据和方法
}
}
}
在 setup 中使用生命周期钩子
import { ref, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const count = ref(0)
let timer = null
onMounted(() => {
// 组件挂载后启动定时器
timer = setInterval(() => {
count.value++
}, 1000)
})
onUnmounted(() => {
// 组件卸载前清除定时器
if (timer) {
clearInterval(timer)
}
})
return {
count
}
}
}
组件通信
父子组件通信
// 父组件
import { ref } from 'vue'
export default {
setup() {
const message = ref('Hello from parent')
const sendMessageToChild = (msg) => {
console.log(`父组件收到消息: ${msg}`)
}
return {
message,
sendMessageToChild
}
}
}
// 子组件
import { defineProps, defineEmits } from 'vue'
export default {
props: {
message: String
},
emits: ['update-message'],
setup(props, { emit }) {
const updateMessage = () => {
emit('update-message', 'Hello from child')
}
return {
updateMessage
}
}
}
兄弟组件通信
// 使用 provide 和 inject 实现跨层级通信
import { provide, inject, ref } from 'vue'
// 祖先组件
export default {
setup() {
const sharedData = ref('Shared data')
provide('sharedData', sharedData)
provide('updateSharedData', (newData) => {
sharedData.value = newData
})
return {
sharedData
}
}
}
// 后代组件
export default {
setup() {
const sharedData = inject('sharedData')
const updateSharedData = inject('updateSharedData')
const changeData = () => {
updateSharedData('New data from child')
}
return {
sharedData,
changeData
}
}
}
组合函数(Composable Functions)
创建可复用的逻辑
// 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, reactive, computed } from 'vue'
export function useApi(url) {
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)
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 = () => fetchData()
const hasData = computed(() => data.value !== null)
return {
data,
loading,
error,
fetchData,
refresh,
hasData
}
}
// 使用示例
export default {
setup() {
const { data, loading, error, fetchData } = useApi('/api/users')
fetchData()
return {
data,
loading,
error,
fetchData
}
}
}
状态管理最佳实践
复杂组件的状态管理
// composables/useForm.js
import { ref, reactive, computed } from 'vue'
export function useForm(initialData = {}) {
const form = reactive({ ...initialData })
const errors = reactive({})
const isSubmitting = ref(false)
const isValid = computed(() => {
return Object.keys(errors).length === 0
})
const validateField = (field, value) => {
// 简单的验证逻辑
if (!value) {
errors[field] = `${field} 是必填项`
return false
}
delete errors[field]
return true
}
const validateForm = () => {
Object.keys(form).forEach(field => {
validateField(field, form[field])
})
return isValid.value
}
const submit = async (submitFn) => {
if (!validateForm()) {
return false
}
isSubmitting.value = true
try {
await submitFn(form)
return true
} catch (err) {
console.error('提交失败:', err)
return false
} finally {
isSubmitting.value = false
}
}
const reset = () => {
Object.keys(form).forEach(key => {
form[key] = initialData[key] || ''
})
Object.keys(errors).forEach(key => {
delete errors[key]
})
}
return {
form,
errors,
isSubmitting,
isValid,
validateField,
validateForm,
submit,
reset
}
}
// 在组件中使用
export default {
setup() {
const {
form,
errors,
isSubmitting,
isValid,
validateField,
submit,
reset
} = useForm({
name: '',
email: '',
message: ''
})
const handleInput = (field, value) => {
form[field] = value
validateField(field, value)
}
const handleSubmit = async (formData) => {
// 处理表单提交
console.log('提交数据:', formData)
// 这里可以调用 API
}
return {
form,
errors,
isSubmitting,
isValid,
handleInput,
submit: (e) => submit(handleSubmit),
reset
}
}
}
状态持久化
// 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
}
// 使用示例
export default {
setup() {
const userPreferences = useLocalStorage('userPreferences', {
theme: 'light',
language: 'zh-CN'
})
const toggleTheme = () => {
userPreferences.value.theme =
userPreferences.value.theme === 'light' ? 'dark' : 'light'
}
return {
userPreferences,
toggleTheme
}
}
}
性能优化技巧
避免不必要的计算
import { computed, shallowRef } from 'vue'
// 对于大型对象,使用 shallowRef 可以避免深度监听
const largeObject = shallowRef({
data: new Array(10000).fill(0),
metadata: {}
})
// 使用 computed 缓存计算结果
const expensiveCalculation = computed(() => {
// 复杂的计算逻辑
return someExpensiveOperation(largeObject.value.data)
})
条件渲染和动态组件
import { ref, defineAsyncComponent } from 'vue'
export default {
setup() {
const currentComponent = ref('ComponentA')
// 异步加载组件
const AsyncComponent = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
return {
currentComponent,
AsyncComponent
}
}
}
实际项目应用示例
完整的用户管理系统
<template>
<div class="user-management">
<h2>用户管理</h2>
<!-- 用户列表 -->
<div class="user-list">
<div v-for="user in users" :key="user.id" class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button @click="deleteUser(user.id)">删除</button>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading">加载中...</div>
<!-- 错误处理 -->
<div v-if="error" class="error">{{ error }}</div>
<!-- 分页 -->
<div class="pagination">
<button @click="prevPage" :disabled="currentPage <= 1">上一页</button>
<span>第 {{ currentPage }} 页</span>
<button @click="nextPage" :disabled="currentPage >= totalPages">下一页</button>
</div>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'
export default {
name: 'UserManagement',
setup() {
const currentPage = ref(1)
const pageSize = ref(10)
const { data: users, loading, error, fetchData } = useApi('/api/users')
const totalPages = computed(() => {
if (!users.value) return 0
return Math.ceil(users.value.length / pageSize.value)
})
const paginatedUsers = computed(() => {
if (!users.value) return []
const start = (currentPage.value - 1) * pageSize.value
return users.value.slice(start, start + pageSize.value)
})
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++
}
}
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--
}
}
const deleteUser = async (userId) => {
try {
await fetch(`/api/users/${userId}`, { method: 'DELETE' })
// 重新获取用户列表
fetchData()
} catch (err) {
console.error('删除用户失败:', err)
}
}
onMounted(() => {
fetchData()
})
return {
users: paginatedUsers,
loading,
error,
currentPage,
totalPages,
nextPage,
prevPage,
deleteUser
}
}
}
</script>
<style scoped>
.user-management {
padding: 20px;
}
.user-card {
border: 1px solid #ccc;
margin: 10px 0;
padding: 15px;
border-radius: 5px;
}
.pagination {
text-align: center;
margin-top: 20px;
}
.error {
color: red;
padding: 10px;
background-color: #ffebee;
border-radius: 5px;
}
</style>
总结
Vue 3 的 Composition API 为前端开发带来了更加灵活和强大的组件状态管理方式。通过本文的介绍,我们了解了:
- 基础概念:理解了
ref、reactive、computed、watch等核心 API - 生命周期管理:掌握了如何在 Composition API 中使用生命周期钩子
- 组件通信:学习了父子组件、兄弟组件以及跨层级组件的通信方式
- 组合函数:通过自定义组合函数实现逻辑复用
- 最佳实践:掌握了状态管理、性能优化等实际应用技巧
Composition API 的引入让 Vue 3 在处理复杂组件逻辑时更加得心应手,特别是在需要在多个组件间共享和重用逻辑的场景下表现尤为突出。随着项目的复杂度增加,Composition API 的优势会更加明显。
建议开发者在项目中逐步采用 Composition API,特别是在重构现有项目或开发新功能时,可以先从简单的组合函数开始,逐步深入理解其强大功能。同时,结合 TypeScript 使用 Composition API 能够获得更好的类型安全和开发体验。
通过持续的实践和学习,相信每位开发者都能熟练掌握 Vue 3 Composition API,并将其运用到实际项目中,构建出更加高效、可维护的前端应用。

评论 (0)