前言
Vue 3的发布带来了革命性的变化,其中最引人注目的就是Composition API的引入。作为Vue 3的核心特性之一,Composition API重新定义了我们编写Vue组件的方式,提供了更加灵活和强大的代码组织能力。本文将深入探讨Composition API的核心概念、基础语法,并通过丰富的实战案例展示如何运用组合式API构建可复用、可维护的Vue组件。
什么是Composition API
传统Options API的局限性
在Vue 2中,我们主要使用Options API来组织组件逻辑。这种方式虽然简单直观,但在处理复杂组件时存在一些明显的局限性:
// Vue 2 Options API示例
export default {
data() {
return {
count: 0,
name: 'Vue'
}
},
computed: {
reversedName() {
return this.name.split('').reverse().join('')
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log('组件已挂载')
}
}
当组件逻辑变得复杂时,Options API会导致代码分散在不同的选项中,难以维护和复用。
Composition API的优势
Composition API通过将相关的逻辑组织在一起,解决了上述问题。它允许我们以函数的形式组织组件逻辑,使得代码更加模块化和可复用。
// Vue 3 Composition API示例
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('Vue')
const reversedName = computed(() => {
return name.value.split('').reverse().join('')
})
const increment = () => {
count.value++
}
onMounted(() => {
console.log('组件已挂载')
})
// 返回给模板使用的响应式数据和方法
return {
count,
name,
reversedName,
increment
}
}
}
Composition API基础语法详解
响应式API基础
ref和reactive
ref和reactive是Composition API中最基础的两个响应式API:
import { ref, reactive } from 'vue'
// 使用ref创建响应式数据
const count = ref(0)
const name = ref('Vue')
// 使用reactive创建响应式对象
const state = reactive({
count: 0,
name: 'Vue'
})
// 在模板中使用时,ref需要.value访问
// 在模板中使用时,reactive直接访问
computed和watch
import { ref, computed, watch } from 'vue'
const count = ref(0)
const name = ref('Vue')
// 计算属性
const doubledCount = computed(() => count.value * 2)
const reversedName = computed({
get: () => name.value.split('').reverse().join(''),
set: (newValue) => {
name.value = newValue.split('').reverse().join('')
}
})
// 监听器
watch(count, (newVal, oldVal) => {
console.log(`count从${oldVal}变为${newVal}`)
})
// 监听多个响应式数据
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
})
生命周期钩子
Composition API提供了与Vue 2相同的生命周期钩子:
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
export default {
setup() {
onBeforeMount(() => {
console.log('组件即将挂载')
})
onMounted(() => {
console.log('组件已挂载')
})
onBeforeUpdate(() => {
console.log('组件即将更新')
})
onUpdated(() => {
console.log('组件已更新')
})
onBeforeUnmount(() => {
console.log('组件即将卸载')
})
onUnmounted(() => {
console.log('组件已卸载')
})
}
}
实战案例:构建一个完整的用户管理系统
基础组件结构
让我们通过一个完整的用户管理系统来演示Composition API的强大功能:
<template>
<div class="user-management">
<div class="header">
<h2>用户管理系统</h2>
<button @click="showAddForm = !showAddForm">
{{ showAddForm ? '取消添加' : '添加用户' }}
</button>
</div>
<!-- 添加用户表单 -->
<div v-if="showAddForm" class="add-form">
<form @submit.prevent="handleSubmit">
<input v-model="newUser.name" placeholder="用户名" required />
<input v-model="newUser.email" type="email" placeholder="邮箱" required />
<input v-model="newUser.role" placeholder="角色" required />
<button type="submit">添加用户</button>
</form>
</div>
<!-- 用户列表 -->
<div class="user-list">
<div class="search-bar">
<input v-model="searchQuery" placeholder="搜索用户..." />
<select v-model="filterRole">
<option value="">所有角色</option>
<option value="admin">管理员</option>
<option value="user">普通用户</option>
</select>
</div>
<div class="users-grid">
<div
v-for="user in filteredUsers"
:key="user.id"
class="user-card"
>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<span class="role">{{ user.role }}</span>
<div class="actions">
<button @click="editUser(user)">编辑</button>
<button @click="deleteUser(user.id)">删除</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
export default {
name: 'UserManagement',
setup() {
// 响应式数据
const users = ref([])
const showAddForm = ref(false)
const newUser = ref({
name: '',
email: '',
role: ''
})
const searchQuery = ref('')
const filterRole = ref('')
const editingUser = ref(null)
// 计算属性
const filteredUsers = computed(() => {
return users.value.filter(user => {
const matchesSearch = user.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
user.email.toLowerCase().includes(searchQuery.value.toLowerCase())
const matchesRole = !filterRole.value || user.role === filterRole.value
return matchesSearch && matchesRole
})
})
// 方法
const fetchUsers = async () => {
try {
// 模拟API调用
const response = await fetch('/api/users')
users.value = await response.json()
} catch (error) {
console.error('获取用户失败:', error)
}
}
const handleSubmit = async () => {
if (!newUser.value.name || !newUser.value.email || !newUser.value.role) {
return
}
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newUser.value)
})
const createdUser = await response.json()
users.value.push(createdUser)
newUser.value = { name: '', email: '', role: '' }
showAddForm.value = false
} catch (error) {
console.error('添加用户失败:', error)
}
}
const editUser = (user) => {
editingUser.value = { ...user }
}
const deleteUser = async (userId) => {
if (confirm('确定要删除这个用户吗?')) {
try {
await fetch(`/api/users/${userId}`, {
method: 'DELETE'
})
users.value = users.value.filter(user => user.id !== userId)
} catch (error) {
console.error('删除用户失败:', error)
}
}
}
// 生命周期钩子
onMounted(() => {
fetchUsers()
})
// 返回给模板使用的数据和方法
return {
users,
showAddForm,
newUser,
searchQuery,
filterRole,
filteredUsers,
handleSubmit,
editUser,
deleteUser
}
}
}
</script>
<style scoped>
.user-management {
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.add-form {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
.search-bar {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.users-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.user-card {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
background: #f9f9f9;
}
.role {
display: inline-block;
padding: 4px 8px;
background: #007bff;
color: white;
border-radius: 4px;
font-size: 12px;
}
.actions {
margin-top: 10px;
}
.actions button {
margin-right: 5px;
padding: 5px 10px;
border: none;
border-radius: 3px;
cursor: pointer;
}
</style>
高级模式:自定义组合式函数
创建可复用的组合式函数
组合式函数是Composition API最强大的特性之一,它允许我们将组件逻辑封装成可复用的函数:
// composables/useApi.js
import { ref, reactive } 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 postData = async (newData) => {
loading.value = true
error.value = null
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newData)
})
const result = await response.json()
data.value = result
return result
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
fetchData,
postData
}
}
// 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
}
使用自定义组合式函数
<template>
<div class="dashboard">
<div v-if="loading">加载中...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else>
<h2>用户统计</h2>
<div class="stats">
<div class="stat-card">
<h3>总用户数</h3>
<p>{{ data?.totalUsers }}</p>
</div>
<div class="stat-card">
<h3>活跃用户</h3>
<p>{{ data?.activeUsers }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
import { useApi } from '@/composables/useApi'
import { useLocalStorage } from '@/composables/useLocalStorage'
export default {
setup() {
const { data, loading, error, fetchData } = useApi('/api/dashboard')
const theme = useLocalStorage('theme', 'light')
// 组件挂载时获取数据
fetchData()
return {
data,
loading,
error,
theme
}
}
}
</script>
复杂组件设计模式
模态框组件模式
<template>
<div class="modal-overlay" v-if="visible" @click="handleOverlayClick">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>{{ title }}</h3>
<button @click="close" class="close-btn">×</button>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<button @click="close">取消</button>
<button @click="confirm" class="confirm-btn">确认</button>
</div>
</div>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '提示'
}
},
emits: ['close', 'confirm'],
setup(props, { emit }) {
const visible = ref(props.visible)
watch(() => props.visible, (newVal) => {
visible.value = newVal
})
const close = () => {
visible.value = false
emit('close')
}
const confirm = () => {
emit('confirm')
}
const handleOverlayClick = (e) => {
if (e.target === e.currentTarget) {
close()
}
}
return {
visible,
close,
confirm,
handleOverlayClick
}
}
}
</script>
表单验证模式
// composables/useFormValidation.js
import { ref, reactive } from 'vue'
export function useFormValidation(initialData, rules) {
const formData = reactive({ ...initialData })
const errors = ref({})
const isValid = ref(true)
const validateField = (fieldName, value) => {
const fieldRules = rules[fieldName]
if (!fieldRules) return true
for (const rule of fieldRules) {
if (rule.required && !value) {
errors.value[fieldName] = rule.message || `${fieldName}是必填项`
return false
}
if (rule.pattern && !rule.pattern.test(value)) {
errors.value[fieldName] = rule.message || `${fieldName}格式不正确`
return false
}
}
delete errors.value[fieldName]
return true
}
const validateAll = () => {
let allValid = true
for (const field in rules) {
if (!validateField(field, formData[field])) {
allValid = false
}
}
isValid.value = allValid
return allValid
}
const setFieldValue = (field, value) => {
formData[field] = value
validateField(field, value)
}
return {
formData,
errors,
isValid,
validateAll,
setFieldValue
}
}
// 使用示例
export default {
setup() {
const initialData = {
name: '',
email: '',
password: ''
}
const rules = {
name: [
{ required: true, message: '姓名不能为空' }
],
email: [
{ required: true, message: '邮箱不能为空' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: '邮箱格式不正确' }
],
password: [
{ required: true, message: '密码不能为空' },
{ pattern: /^.{6,}$/, message: '密码至少6位' }
]
}
const { formData, errors, isValid, validateAll, setFieldValue } = useFormValidation(initialData, rules)
const handleSubmit = () => {
if (validateAll()) {
console.log('表单提交:', formData)
} else {
console.log('表单验证失败')
}
}
return {
formData,
errors,
isValid,
handleSubmit,
setFieldValue
}
}
}
性能优化最佳实践
避免不必要的计算
// 不好的做法
const expensiveValue = computed(() => {
// 复杂的计算逻辑
return someComplexOperation(data.value)
})
// 好的做法 - 使用缓存
import { computed } from 'vue'
const expensiveValue = computed({
get: () => {
// 复杂的计算逻辑
return someComplexOperation(data.value)
},
set: (newValue) => {
// 设置逻辑
}
})
合理使用watch
// 避免过度监听
const watchOptions = {
immediate: false, // 只在值改变时触发
deep: false, // 不深度监听
flush: 'post' // 在DOM更新后触发
}
watch(source, callback, watchOptions)
组件通信优化
<template>
<div>
<child-component
:user-data="userData"
@update-user="handleUserUpdate"
/>
</div>
</template>
<script>
import { ref, computed } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
setup() {
const userData = ref({
name: 'John',
email: 'john@example.com'
})
const handleUserUpdate = (updatedData) => {
userData.value = { ...userData.value, ...updatedData }
}
return {
userData,
handleUserUpdate
}
}
}
</script>
与Vue 2的兼容性处理
迁移指南
// Vue 2组件迁移至Vue 3
// Vue 2
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
// Vue 3 Composition API
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
}
混合使用模式
// 在Vue 3中同时使用Options API和Composition API
export default {
name: 'MixedComponent',
data() {
return {
legacyData: 'old way'
}
},
setup() {
const modernData = ref('new way')
return {
modernData
}
}
}
总结
Vue 3的Composition API为前端开发带来了革命性的变化。通过将相关的逻辑组织在一起,它使得代码更加模块化、可复用和易于维护。本文从基础语法到高级模式,从实战案例到性能优化,全面展示了Composition API的强大功能。
通过学习和实践Composition API,开发者可以:
- 提高代码复用性:通过自定义组合式函数,将通用逻辑封装成可复用的模块
- 增强代码可维护性:将相关的逻辑组织在一起,避免了Options API中逻辑分散的问题
- 提升开发效率:更灵活的代码组织方式,使得复杂组件的开发变得更加简单
- 改善团队协作:清晰的代码结构有助于团队成员更好地理解和维护代码
在实际项目中,建议根据具体需求选择合适的API使用方式。对于简单的组件,可以继续使用Options API;对于复杂的组件,Composition API能够提供更好的解决方案。同时,随着Vue 3生态的不断完善,我们期待看到更多基于Composition API的优秀实践和工具出现。
掌握Composition API不仅能够提升个人开发技能,更是适应现代前端开发趋势的必要能力。随着Vue生态的持续发展,Composition API必将在未来的前端开发中发挥越来越重要的作用。

评论 (0)