前言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性为开发者提供了更加灵活和强大的组件开发方式,特别是在处理复杂组件逻辑时表现尤为突出。本文将深入探讨 Vue 3 Composition API 的核心概念、使用方法以及最佳实践,帮助开发者从基础语法逐步掌握这一现代化的开发模式。
什么是 Composition API
理念与优势
Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式,它允许我们以函数的形式组织和重用组件逻辑。相比于 Vue 2 中的选项式 API(Options API),Composition API 具有以下显著优势:
- 更好的逻辑复用:通过组合函数(composables)实现逻辑复用
- 更灵活的代码组织:按功能而不是按选项类型组织代码
- 更强的类型支持:与 TypeScript 配合使用时提供更好的类型推断
- 更清晰的组件结构:将相关的逻辑集中在一起
与 Options API 的对比
让我们通过一个简单的例子来理解两者的区别:
// Vue 2 Options API
export default {
data() {
return {
count: 0,
message: 'Hello'
}
},
methods: {
increment() {
this.count++
},
decrement() {
this.count--
}
},
computed: {
doubledCount() {
return this.count * 2
}
}
}
// Vue 3 Composition API
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(0)
const message = ref('Hello')
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const doubledCount = computed(() => count.value * 2)
return {
count,
message,
increment,
decrement,
doubledCount
}
}
}
响应式 API 核心概念
ref 和 reactive 的基本使用
在 Composition API 中,响应式数据的创建主要通过 ref 和 reactive 两个核心函数来实现。
ref 的使用
ref 用于创建响应式的数据引用,它会自动包装基础类型为对象:
import { ref } from 'vue'
// 创建基础类型的响应式变量
const count = ref(0)
const name = ref('Vue')
// 访问和修改值
console.log(count.value) // 0
count.value = 10
console.log(count.value) // 10
// 在模板中使用
// <template>
// <p>{{ count }}</p>
// </template>
reactive 的使用
reactive 用于创建响应式对象,适用于复杂数据结构:
import { reactive } from 'vue'
const state = reactive({
count: 0,
name: 'Vue',
user: {
firstName: 'John',
lastName: 'Doe'
}
})
// 修改属性
state.count = 10
state.user.firstName = 'Jane'
// 在模板中使用
// <template>
// <p>{{ state.count }}</p>
// <p>{{ state.user.firstName }}</p>
// </template>
toRefs 和 toRef 的区别
当需要将响应式对象的属性解构时,toRefs 和 toRef 提供了不同的解决方案:
import { reactive, toRefs } from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
// 使用 toRefs 解构
const { count, name } = toRefs(state)
// 这样解构出来的 count 和 name 仍然是响应式的
// 如果直接解构,会失去响应性
const { count, name } = state // 这样解构后不再是响应式的
生命周期钩子的使用
setup 函数中的生命周期
在 Composition API 中,我们通过 setup 函数来组织组件逻辑,并使用特定的函数来处理生命周期:
import { onMounted, onUpdated, onUnmounted } from 'vue'
export default {
setup() {
// 组件挂载时执行
onMounted(() => {
console.log('组件已挂载')
})
// 组件更新时执行
onUpdated(() => {
console.log('组件已更新')
})
// 组件卸载前执行
onUnmounted(() => {
console.log('组件即将卸载')
})
return {
// 返回的数据和方法
}
}
}
完整的生命周期示例
import { ref, onMounted, onUpdated, onUnmounted, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const message = ref('Hello')
// 挂载时执行
onMounted(() => {
console.log('组件已挂载')
// 可以在这里执行 DOM 操作
})
// 更新时执行
onUpdated(() => {
console.log('组件已更新')
})
// 卸载前执行
onUnmounted(() => {
console.log('组件即将卸载')
})
// 监听响应式数据变化
watch(count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${newValue}`)
})
const increment = () => {
count.value++
}
return {
count,
message,
increment
}
}
}
组合函数(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 doubled = computed(() => count.value * 2)
return {
count,
increment,
decrement,
reset,
doubled
}
}
// composables/useFetch.js
import { ref, watch } from 'vue'
export function useFetch(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
}
}
// 立即执行一次
fetchData()
// 监听 URL 变化,重新获取数据
watch(url, fetchData)
return {
data,
loading,
error,
refetch: fetchData
}
}
使用组合函数
import { useCounter } from '@/composables/useCounter'
import { useFetch } from '@/composables/useFetch'
export default {
setup() {
// 使用计数器组合函数
const { count, increment, decrement, doubled } = useCounter(0)
// 使用数据获取组合函数
const { data, loading, error, refetch } = useFetch('/api/users')
return {
count,
increment,
decrement,
doubled,
data,
loading,
error,
refetch
}
}
}
组件通信与数据传递
父子组件通信
在 Composition API 中,父子组件通信依然遵循 Vue 的标准模式:
// 父组件
import { ref } from 'vue'
export default {
setup() {
const parentMessage = ref('来自父组件的消息')
const handleChildEvent = (message) => {
console.log('收到子组件消息:', message)
}
return {
parentMessage,
handleChildEvent
}
}
}
// 子组件
export default {
props: ['message'],
emits: ['child-event'],
setup(props, { emit }) {
const childMessage = ref('来自子组件的消息')
const sendToParent = () => {
emit('child-event', childMessage.value)
}
return {
childMessage,
sendToParent
}
}
}
使用 provide 和 inject 实现跨层级通信
// 父组件
import { provide, ref } from 'vue'
export default {
setup() {
const theme = ref('dark')
const user = ref({ name: 'John', role: 'admin' })
// 提供数据给后代组件
provide('theme', theme)
provide('user', user)
return {
theme,
user
}
}
}
// 子组件
import { inject } from 'vue'
export default {
setup() {
// 注入提供的数据
const theme = inject('theme')
const user = inject('user')
return {
theme,
user
}
}
}
复杂状态管理实战
创建全局状态管理器
// stores/useGlobalStore.js
import { reactive, readonly } from 'vue'
// 创建全局状态
const state = reactive({
user: null,
isLoggedIn: false,
theme: 'light',
notifications: []
})
// 提供状态访问方法
export function useGlobalStore() {
const getUser = () => state.user
const getIsLoggedIn = () => state.isLoggedIn
const getTheme = () => state.theme
const getNotifications = () => state.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)
}
}
// 返回只读状态和方法
return {
state: readonly(state),
getUser,
getIsLoggedIn,
getTheme,
getNotifications,
setUser,
setTheme,
addNotification,
removeNotification
}
}
在组件中使用全局状态
import { useGlobalStore } from '@/stores/useGlobalStore'
import { computed } from 'vue'
export default {
setup() {
const {
state,
getUser,
getIsLoggedIn,
getTheme,
addNotification,
setUser
} = useGlobalStore()
// 计算属性
const currentUser = computed(() => getUser())
const isLoggedIn = computed(() => getIsLoggedIn())
const currentTheme = computed(() => getTheme())
// 处理登录
const handleLogin = async (credentials) => {
try {
// 模拟 API 调用
const user = await login(credentials)
setUser(user)
addNotification({
type: 'success',
message: '登录成功'
})
} catch (error) {
addNotification({
type: 'error',
message: '登录失败'
})
}
}
return {
state,
currentUser,
isLoggedIn,
currentTheme,
handleLogin
}
}
}
性能优化与最佳实践
合理使用计算属性和监听器
import { ref, computed, watch, watchEffect } from 'vue'
export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')
const age = ref(25)
// 使用 computed 创建计算属性
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// 使用 watch 监听特定数据变化
watch(firstName, (newVal, oldVal) => {
console.log(`firstName 从 ${oldVal} 变为 ${newVal}`)
})
// 使用 watchEffect 自动追踪依赖
const fullNameWithAge = computed(() => {
return `${firstName.value} ${lastName.value} (${age.value})`
})
// 监听多个数据源
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`姓名从 ${oldFirst} ${oldLast} 变为 ${newFirst} ${newLast}`)
})
return {
firstName,
lastName,
age,
fullName,
fullNameWithAge
}
}
}
组件性能优化技巧
import { ref, computed, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const data = ref([])
const loading = ref(false)
// 使用懒加载避免不必要的计算
const expensiveComputed = computed(() => {
// 只有当需要时才执行昂贵的计算
return data.value.map(item => ({
...item,
processed: item.name.toUpperCase()
}))
})
// 使用防抖优化高频事件
const debouncedSearch = (searchTerm) => {
// 防抖实现
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
fetchData(searchTerm)
}, 300)
}
let searchTimeout
// 合理的生命周期管理
onMounted(() => {
// 组件挂载时执行初始化操作
loadData()
})
onUnmounted(() => {
// 清理定时器等资源
if (searchTimeout) {
clearTimeout(searchTimeout)
}
})
return {
data,
loading,
expensiveComputed
}
}
}
实战案例:构建一个完整的用户管理系统
组件结构设计
让我们通过一个完整的用户管理系统来演示 Composition API 的实际应用:
// components/UserList.vue
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/useUserStore'
export default {
name: 'UserList',
setup() {
const {
users,
loading,
error,
fetchUsers,
deleteUser,
addUser
} = useUserStore()
const searchTerm = ref('')
const showAddForm = ref(false)
const newUserForm = ref({
name: '',
email: '',
role: 'user'
})
// 过滤用户列表
const filteredUsers = computed(() => {
if (!searchTerm.value) return users.value
return users.value.filter(user =>
user.name.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.value.toLowerCase())
)
})
// 处理删除用户
const handleDeleteUser = async (userId) => {
if (confirm('确定要删除这个用户吗?')) {
try {
await deleteUser(userId)
// 可以添加删除成功的通知
} catch (error) {
console.error('删除用户失败:', error)
}
}
}
// 处理添加新用户
const handleAddUser = async () => {
if (!newUserForm.value.name || !newUserForm.value.email) {
alert('请填写必填字段')
return
}
try {
await addUser(newUserForm.value)
newUserForm.value = { name: '', email: '', role: 'user' }
showAddForm.value = false
} catch (error) {
console.error('添加用户失败:', error)
}
}
// 初始化数据
onMounted(() => {
fetchUsers()
})
return {
users: filteredUsers,
loading,
error,
searchTerm,
showAddForm,
newUserForm,
handleDeleteUser,
handleAddUser
}
}
}
状态管理实现
// stores/useUserStore.js
import { reactive, readonly } from 'vue'
const state = reactive({
users: [],
loading: false,
error: null
})
// API 调用函数(模拟)
const fetchUsersFromAPI = async () => {
// 模拟 API 调用
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ id: 1, name: '张三', email: 'zhangsan@example.com', role: 'admin' },
{ id: 2, name: '李四', email: 'lisi@example.com', role: 'user' }
])
}, 1000)
})
}
const deleteUserFromAPI = async (id) => {
return new Promise(resolve => {
setTimeout(() => resolve({ success: true }), 500)
})
}
const addUserToAPI = async (userData) => {
return new Promise(resolve => {
setTimeout(() => resolve({ id: Date.now(), ...userData }), 500)
})
}
export function useUserStore() {
const fetchUsers = async () => {
state.loading = true
state.error = null
try {
const users = await fetchUsersFromAPI()
state.users = users
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
}
const deleteUser = async (id) => {
state.loading = true
try {
await deleteUserFromAPI(id)
state.users = state.users.filter(user => user.id !== id)
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
}
const addUser = async (userData) => {
state.loading = true
try {
const newUser = await addUserToAPI(userData)
state.users.push(newUser)
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
}
return {
state: readonly(state),
fetchUsers,
deleteUser,
addUser
}
}
使用组合函数优化
// composables/useForm.js
import { ref, reactive } from 'vue'
export function useForm(initialValues = {}) {
const form = reactive({ ...initialValues })
const errors = ref({})
const isSubmitting = ref(false)
const validate = (rules) => {
const newErrors = {}
Object.keys(rules).forEach(field => {
const rule = rules[field]
const value = form[field]
if (rule.required && !value) {
newErrors[field] = `${field} 是必填项`
} else if (rule.pattern && !rule.pattern.test(value)) {
newErrors[field] = rule.message || '格式不正确'
}
})
errors.value = newErrors
return Object.keys(newErrors).length === 0
}
const reset = () => {
Object.keys(form).forEach(key => {
form[key] = initialValues[key] || ''
})
errors.value = {}
}
const submit = async (submitFn) => {
if (!validate()) return false
isSubmitting.value = true
try {
const result = await submitFn(form)
reset()
return result
} catch (error) {
console.error('提交失败:', error)
return false
} finally {
isSubmitting.value = false
}
}
return {
form,
errors,
isSubmitting,
validate,
reset,
submit
}
}
总结与展望
Vue 3 Composition API 的引入为前端开发带来了革命性的变化。通过本文的详细介绍,我们看到了:
- 响应式系统的强大:ref 和 reactive 提供了灵活的数据响应能力
- 生命周期管理的清晰性:通过特定的钩子函数更好地控制组件行为
- 逻辑复用的便利性:组合函数让代码复用变得更加简单和优雅
- 状态管理的现代化:结合组合函数可以构建出更加可维护的状态管理系统
在实际开发中,建议:
- 从简单的场景开始使用 Composition API
- 合理设计组合函数,保持其职责单一
- 注意性能优化,避免不必要的计算和监听
- 结合 TypeScript 使用以获得更好的类型安全
随着 Vue 生态系统的不断发展,Composition API 必将成为现代 Vue 开发的标准实践。掌握这一技术不仅能够提高开发效率,还能让代码更加清晰、可维护和可测试。
通过本文的学习和实践,相信读者已经对 Vue 3 Composition API 有了深入的理解,并能够在实际项目中灵活运用这些技术来构建高质量的现代化应用。

评论 (0)