前言
Vue 3的发布带来了革命性的变化,其中最引人注目的就是Composition API的引入。相比于Vue 2的选项式API,Composition API为开发者提供了更灵活、更强大的组件状态管理方式。本文将深入探讨Composition API的核心概念和使用方法,帮助开发者构建更灵活、可维护的Vue应用。
什么是Composition API
Composition API是Vue 3中引入的一种新的组件状态管理方式,它将组件的逻辑按照功能进行组织,而不是按照选项(如data、methods、computed等)进行分组。这种设计模式使得代码更加模块化,更容易复用和维护。
Composition API的核心优势
- 更好的逻辑复用:通过组合式函数(Composable Functions)实现逻辑复用
- 更灵活的代码组织:按照功能逻辑组织代码,而非选项类型
- 更好的类型支持:与TypeScript集成更佳
- 更清晰的代码结构:避免了选项式API中的this指向问题
基础概念与核心API
reactive与ref
在Composition API中,响应式数据的创建主要通过reactive和ref两个核心API实现。
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'
}
})
// 使用时需要通过.value访问ref的值
console.log(count.value) // 0
count.value = 10
console.log(count.value) // 10
// reactive对象可以直接访问属性
console.log(state.count) // 0
state.count = 10
console.log(state.count) // 10
computed
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 reversedFullName = computed({
get: () => {
return `${lastName.value} ${firstName.value}`
},
set: (value) => {
const names = value.split(' ')
firstName.value = names[0]
lastName.value = names[1]
}
})
console.log(fullName.value) // John Doe
reversedFullName.value = 'Smith John'
console.log(firstName.value) // John
console.log(lastName.value) // Smith
watch与watchEffect
watch和watchEffect用于监听响应式数据的变化:
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const name = ref('Vue')
// 基本watch用法
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}`)
console.log(`当前name值: ${name.value}`)
})
// 停止监听
const stop = watch(count, (newVal) => {
console.log(`监听到变化: ${newVal}`)
})
// 调用stop()停止监听
// stop()
生命周期钩子
Composition API提供了与Vue 2选项式API对应的生命周期钩子:
import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue'
export default {
setup() {
// 组件挂载时调用
onMounted(() => {
console.log('组件已挂载')
// 可以在这里执行DOM操作
})
// 组件更新时调用
onUpdated(() => {
console.log('组件已更新')
})
// 组件卸载前调用
onBeforeUnmount(() => {
console.log('组件即将卸载')
// 清理定时器、事件监听器等
})
return {
// 返回的数据和方法
}
}
}
组合式函数(Composables)实战
组合式函数是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
}
}
在组件中使用组合式函数
<template>
<div>
<p>计数: {{ count }}</p>
<p>双倍计数: {{ doubleCount }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<button @click="reset">重置</button>
</div>
</template>
<script>
import { useCounter } from '@/composables/useCounter'
export default {
setup() {
const { count, increment, decrement, reset, doubleCount } = useCounter(10)
return {
count,
increment,
decrement,
reset,
doubleCount
}
}
}
</script>
复杂的组合式函数示例
// composables/useApi.js
import { ref, reactive, watch } from 'vue'
export function useApi(url, options = {}) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const response = reactive({
status: null,
headers: null
})
const fetchData = async () => {
loading.value = true
error.value = null
try {
const res = await fetch(url, options)
response.status = res.status
response.headers = Object.fromEntries(res.headers.entries())
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`)
}
data.value = await res.json()
} catch (err) {
error.value = err.message
console.error('API请求失败:', err)
} finally {
loading.value = false
}
}
// 自动执行请求
if (options.autoFetch !== false) {
fetchData()
}
// 监听url变化,自动重新请求
watch(url, fetchData)
return {
data,
loading,
error,
response,
refetch: fetchData
}
}
复杂组件状态管理
多层级组件的状态管理
在复杂的组件树中,状态管理变得尤为重要。我们可以通过组合式函数来管理跨组件的状态:
// composables/useUser.js
import { ref, reactive } from 'vue'
// 全局用户状态
const user = reactive({
id: null,
name: '',
email: '',
isLoggedIn: false
})
const setUser = (userData) => {
Object.assign(user, userData)
}
const clearUser = () => {
user.id = null
user.name = ''
user.email = ''
user.isLoggedIn = false
}
export function useUser() {
return {
user: readonly(user),
setUser,
clearUser
}
}
表单状态管理
// composables/useForm.js
import { reactive, computed } from 'vue'
export function useForm(initialData = {}) {
const formData = reactive({ ...initialData })
const errors = reactive({})
const isSubmitting = ref(false)
const isValid = computed(() => {
return Object.keys(errors).length === 0
})
const validateField = (field, value) => {
// 简单的验证规则
if (field === 'email' && value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
errors[field] = '请输入有效的邮箱地址'
} else if (field === 'password' && value && value.length < 6) {
errors[field] = '密码长度至少6位'
} else {
delete errors[field]
}
}
const validateAll = () => {
Object.keys(formData).forEach(field => {
validateField(field, formData[field])
})
}
const setField = (field, value) => {
formData[field] = value
validateField(field, value)
}
const submit = async (submitHandler) => {
if (!isValid.value) {
validateAll()
return false
}
isSubmitting.value = true
try {
const result = await submitHandler(formData)
return result
} catch (error) {
console.error('提交失败:', error)
return false
} finally {
isSubmitting.value = false
}
}
return {
formData,
errors,
isValid,
isSubmitting,
setField,
submit
}
}
实际应用案例
实时搜索功能
<template>
<div>
<input
v-model="searchQuery"
placeholder="搜索..."
@input="debouncedSearch"
/>
<div v-if="loading">搜索中...</div>
<div v-else-if="error">{{ error }}</div>
<ul v-else>
<li v-for="item in searchResults" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
import { ref, watch } from 'vue'
import { useApi } from '@/composables/useApi'
export default {
setup() {
const searchQuery = ref('')
const searchResults = ref([])
const loading = ref(false)
const error = ref(null)
const debouncedSearch = debounce(async () => {
if (!searchQuery.value.trim()) {
searchResults.value = []
return
}
loading.value = true
error.value = null
try {
const response = await fetch(`/api/search?q=${searchQuery.value}`)
if (!response.ok) {
throw new Error('搜索失败')
}
searchResults.value = await response.json()
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}, 300)
// 清除搜索
watch(searchQuery, () => {
if (!searchQuery.value.trim()) {
searchResults.value = []
}
})
return {
searchQuery,
searchResults,
loading,
error,
debouncedSearch
}
}
}
// 防抖函数
function debounce(func, wait) {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
</script>
模态框组件
<template>
<div class="modal-overlay" v-if="isVisible" @click="close">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>{{ title }}</h3>
<button @click="close">×</button>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<button @click="close">取消</button>
<button @click="confirm">确认</button>
</div>
</div>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
props: {
title: String,
visible: Boolean
},
emits: ['confirm', 'close'],
setup(props, { emit }) {
const isVisible = ref(false)
watch(() => props.visible, (newVal) => {
isVisible.value = newVal
})
const close = () => {
isVisible.value = false
emit('close')
}
const confirm = () => {
emit('confirm')
close()
}
return {
isVisible,
close,
confirm
}
}
}
</script>
最佳实践与性能优化
合理使用响应式API
// ❌ 不好的做法
export default {
setup() {
const state = reactive({
user: null,
posts: [],
loading: false,
error: null
})
// 每次都创建新的函数
const fetchUser = () => {
// ...
}
const fetchPosts = () => {
// ...
}
return {
...state,
fetchUser,
fetchPosts
}
}
}
// ✅ 好的做法
export default {
setup() {
const user = ref(null)
const posts = ref([])
const loading = ref(false)
const error = ref(null)
// 使用箭头函数保持this指向
const fetchUser = async () => {
// ...
}
const fetchPosts = async () => {
// ...
}
return {
user,
posts,
loading,
error,
fetchUser,
fetchPosts
}
}
}
组合式函数的命名规范
// ✅ 好的命名规范
// useUser.js - 用户相关逻辑
// useApi.js - API请求逻辑
// useForm.js - 表单处理逻辑
// useStorage.js - 本地存储逻辑
// 组合式函数应该以use开头,遵循命名约定
export function useCounter(initialValue = 0) {
// ...
}
export function useUser() {
// ...
}
export function useTheme() {
// ...
}
避免不必要的响应式转换
// ❌ 不好的做法
const state = reactive({
// 大量不需要响应式的静态数据
staticData: {
// ...
}
})
// ✅ 好的做法
const staticData = {
// 静态数据不需要响应式
}
const state = reactive({
// 只包含需要响应式的动态数据
count: 0,
user: null
})
与Vue 2选项式API的对比
相同点
// Vue 2 选项式API
export default {
data() {
return {
count: 0,
name: 'Vue'
}
},
computed: {
fullName() {
return `${this.name} 3`
}
},
methods: {
increment() {
this.count++
}
}
}
// Vue 3 Composition API
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('Vue')
const fullName = computed(() => {
return `${name.value} 3`
})
const increment = () => {
count.value++
}
return {
count,
name,
fullName,
increment
}
}
}
不同点
- 代码组织方式:Composition API按功能组织,选项式API按类型组织
- 逻辑复用:Composition API通过组合式函数实现复用,选项式API通过mixins实现
- this指向:Composition API中不再需要处理this指向问题
- 类型支持:Composition API与TypeScript集成更佳
总结
Vue 3的Composition API为前端开发者提供了一种更灵活、更强大的组件状态管理方式。通过合理使用ref、reactive、computed、watch等核心API,以及创建可复用的组合式函数,我们可以构建出更加模块化、可维护的Vue应用。
在实际开发中,建议:
- 优先使用Composition API来组织组件逻辑
- 合理封装组合式函数来实现逻辑复用
- 注意性能优化,避免不必要的响应式转换
- 遵循命名规范,保持代码的一致性
随着Vue生态的不断发展,Composition API将成为构建现代Vue应用的标准方式。掌握这些核心概念和最佳实践,将帮助开发者更好地应对复杂的应用开发需求。

评论 (0)