前言
Vue 3 的发布带来了全新的 Composition API,这不仅是对 Vue 2.x 选项式 API 的补充,更是一次革命性的重构。Composition API 通过函数式的方式组织代码逻辑,让开发者能够更好地复用和管理组件状态,特别是在处理复杂组件时展现出强大的优势。
本文将深入探讨 Composition API 的核心概念、使用方法和最佳实践,从基础语法到高级特性,帮助开发者全面掌握现代 Vue 开发模式,提升组件的复用性和可维护性。
一、Composition API 核心概念
1.1 什么是 Composition API
Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式。与传统的选项式 API(Options API)不同,Composition API 允许我们使用函数来组织和复用组件逻辑,而不是将逻辑分散在各种选项中。
// Vue 2 Options API 示例
export default {
data() {
return {
count: 0,
message: 'Hello'
}
},
methods: {
increment() {
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 doubledCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
return {
count,
message,
doubledCount,
increment
}
}
}
1.2 Composition API 的优势
Composition API 主要解决了以下几个问题:
- 逻辑复用:通过函数组合,可以轻松地在多个组件间共享逻辑
- 更好的代码组织:将相关的逻辑组织在一起,而不是分散在不同选项中
- 更灵活的开发体验:可以按功能模块来组织代码,提高可读性
- 类型支持更好:在 TypeScript 中有更好的类型推断支持
二、响应式 API 基础
2.1 ref 和 reactive 的基本使用
import { ref, reactive } from 'vue'
export default {
setup() {
// 基本数据类型
const count = ref(0)
const name = ref('Vue')
// 对象类型
const user = reactive({
firstName: 'John',
lastName: 'Doe',
age: 30
})
// 修改值
const increment = () => {
count.value++
}
const updateName = () => {
name.value = 'Vue 3'
}
const updateUser = () => {
user.age = 31
}
return {
count,
name,
user,
increment,
updateName,
updateUser
}
}
}
2.2 深层嵌套对象的响应式处理
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({
user: {
profile: {
name: 'Alice',
settings: {
theme: 'dark',
notifications: true
}
}
}
})
// 直接修改深层属性
const updateTheme = () => {
state.user.profile.settings.theme = 'light'
}
return {
state,
updateTheme
}
}
}
2.3 toRef 和 toRefs 的使用
import { reactive, toRef, toRefs } from 'vue'
export default {
setup() {
const state = reactive({
firstName: 'John',
lastName: 'Doe',
age: 30
})
// 转换为 ref
const firstNameRef = toRef(state, 'firstName')
// 转换为多个 ref
const { firstName, lastName, age } = toRefs(state)
return {
firstNameRef,
firstName,
lastName,
age
}
}
}
三、生命周期钩子
3.1 生命周期钩子的使用方式
import { onMounted, onUpdated, onUnmounted, onBeforeMount } from 'vue'
export default {
setup() {
// 组件挂载前
onBeforeMount(() => {
console.log('组件即将挂载')
})
// 组件挂载后
onMounted(() => {
console.log('组件已挂载')
// 可以在这里执行 DOM 操作
})
// 组件更新后
onUpdated(() => {
console.log('组件已更新')
})
// 组件卸载前
onUnmounted(() => {
console.log('组件即将卸载')
})
return {}
}
}
3.2 在组合函数中使用生命周期
import { ref, onMounted, onUnmounted } from 'vue'
// 自定义组合函数
function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
// 在组合函数中使用生命周期钩子
onMounted(() => {
console.log('计数器已挂载')
})
return {
count,
increment,
decrement
}
}
export default {
setup() {
const { count, increment, decrement } = useCounter(10)
return {
count,
increment,
decrement
}
}
}
四、计算属性和监听器
4.1 computed 的使用
import { ref, computed } from 'vue'
export default {
setup() {
const firstName = ref('John')
const lastName = ref('Doe')
const age = ref(30)
// 基础计算属性
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 带 setter 的计算属性
const reversedName = computed({
get: () => {
return firstName.value.split('').reverse().join('')
},
set: (value) => {
firstName.value = value.split('').reverse().join('')
}
})
// 复杂计算
const userStatus = computed(() => {
if (age.value < 18) return '未成年'
if (age.value < 60) return '成年人'
return '老年人'
})
return {
firstName,
lastName,
age,
fullName,
reversedName,
userStatus
}
}
}
4.2 watch 的使用
import { ref, watch, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('Vue')
const user = ref({ name: 'John', age: 30 })
// 监听单个 ref
watch(count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${newValue}`)
})
// 监听多个值
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}, name: ${oldName} -> ${newName}`)
})
// 深度监听对象
watch(user, (newValue, oldValue) => {
console.log('用户信息发生变化')
}, { deep: true })
// 立即执行的监听器
watch(count, (newValue) => {
console.log(`立即执行: ${newValue}`)
}, { immediate: true })
// watchEffect - 自动追踪依赖
const watchEffectExample = watchEffect(() => {
console.log(`当前计数: ${count.value}`)
console.log(`当前名称: ${name.value}`)
})
return {
count,
name,
user,
watchEffectExample
}
}
}
五、组件通信
5.1 Props 的传递和验证
import { ref } from 'vue'
export default {
// 定义 props
props: {
title: {
type: String,
required: true,
default: '默认标题'
},
count: {
type: Number,
default: 0,
validator: (value) => value >= 0
},
user: {
type: Object,
default: () => ({})
}
},
setup(props, { emit }) {
const handleClick = () => {
// 触发事件
emit('click', props.count)
}
return {
handleClick
}
}
}
5.2 emits 的使用
import { ref } from 'vue'
export default {
emits: ['update:count', 'delete-item', 'custom-event'],
setup(props, { emit }) {
const count = ref(0)
const increment = () => {
count.value++
// 触发事件
emit('update:count', count.value)
}
const deleteItem = (id) => {
emit('delete-item', id)
}
const customEvent = () => {
emit('custom-event', { message: 'Hello' })
}
return {
count,
increment,
deleteItem,
customEvent
}
}
}
六、自定义组合函数
6.1 创建可复用的逻辑
// 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
}
// 使用示例
import { useLocalStorage } from '@/composables/useLocalStorage'
export default {
setup() {
const theme = useLocalStorage('theme', 'light')
const preferences = useLocalStorage('preferences', {})
return {
theme,
preferences
}
}
}
6.2 复杂的组合函数示例
// 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 () => {
try {
loading.value = true
error.value = null
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()
}
return {
data,
loading,
error,
refresh
}
}
// 使用示例
import { useApi } from '@/composables/useApi'
export default {
setup() {
const { data, loading, error, refresh } = useApi('/api/users')
// 在组件挂载时获取数据
onMounted(() => {
refresh()
})
return {
data,
loading,
error,
refresh
}
}
}
七、高级特性与最佳实践
7.1 条件渲染和动态组件
import { ref, computed } from 'vue'
export default {
setup() {
const currentView = ref('home')
// 动态组件
const dynamicComponent = computed(() => {
switch (currentView.value) {
case 'home':
return 'HomeComponent'
case 'about':
return 'AboutComponent'
case 'contact':
return 'ContactComponent'
default:
return 'HomeComponent'
}
})
const switchView = (view) => {
currentView.value = view
}
return {
currentView,
dynamicComponent,
switchView
}
}
}
7.2 异步组件和动态导入
import { defineAsyncComponent } from 'vue'
export default {
setup() {
// 异步组件
const AsyncComponent = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
return {
AsyncComponent
}
}
}
7.3 错误边界和异常处理
import { ref, onErrorCaptured } from 'vue'
export default {
setup() {
const error = ref(null)
const errorInfo = ref(null)
// 捕获错误
onErrorCaptured((err, instance, info) => {
error.value = err
errorInfo.value = info
console.error('捕获到错误:', err, info)
// 返回 false 阻止错误继续向上传播
return false
})
const throwError = () => {
throw new Error('这是一个测试错误')
}
return {
error,
errorInfo,
throwError
}
}
}
八、性能优化技巧
8.1 使用 computed 缓存
import { ref, computed } from 'vue'
export default {
setup() {
const items = ref([])
// 复杂计算使用 computed 缓存
const expensiveCalculation = computed(() => {
// 模拟耗时操作
console.log('执行复杂计算')
return items.value.reduce((sum, item) => sum + item.value, 0)
})
const addItem = (value) => {
items.value.push({ value })
}
return {
items,
expensiveCalculation,
addItem
}
}
}
8.2 避免不必要的重新计算
import { ref, computed, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const name = ref('Vue')
// 使用 computed 优化性能
const expensiveOperation = computed(() => {
// 只有当 count 改变时才重新计算
return Math.pow(count.value, 2) + count.value * 10
})
// 监听特定值的变化
watch(count, (newCount) => {
console.log(`计数器更新为: ${newCount}`)
})
// 使用 watchEffect 避免重复计算
const computedValue = ref(0)
watchEffect(() => {
// 只有当依赖的值发生变化时才执行
computedValue.value = count.value * 2 + name.value.length
})
return {
count,
name,
expensiveOperation,
computedValue
}
}
}
九、与 Vue 2 的兼容性
9.1 迁移指南
// Vue 2 选项式 API
export default {
data() {
return {
count: 0
}
},
computed: {
doubledCount() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log('组件已挂载')
}
}
// Vue 3 Composition API 等价写法
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const doubledCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
onMounted(() => {
console.log('组件已挂载')
})
return {
count,
doubledCount,
increment
}
}
}
9.2 混合使用模式
import { ref, reactive } from 'vue'
export default {
// 选项式 API 部分
name: 'MixedComponent',
// Composition API 部分
setup() {
const count = ref(0)
return {
count,
increment: () => count.value++
}
},
// 选项式 API 部分
data() {
return {
message: 'Hello'
}
}
}
十、实际项目应用案例
10.1 用户管理组件
<template>
<div class="user-management">
<div class="controls">
<input v-model="searchTerm" placeholder="搜索用户..." />
<button @click="loadUsers">刷新</button>
</div>
<div v-if="loading">加载中...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else>
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{ user.name }} - {{ user.email }}
</li>
</ul>
</div>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'
export default {
setup() {
const searchTerm = ref('')
const users = ref([])
const { data, loading, error, refresh } = useApi('/api/users')
// 过滤用户
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 loadUsers = () => {
refresh()
}
// 监听数据变化
onMounted(() => {
if (data.value) {
users.value = data.value
}
})
return {
searchTerm,
filteredUsers,
loading,
error,
loadUsers
}
}
}
</script>
10.2 表单验证组件
<template>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label>用户名:</label>
<input v-model="formData.username" />
<span v-if="errors.username" class="error">{{ errors.username }}</span>
</div>
<div class="form-group">
<label>邮箱:</label>
<input v-model="formData.email" type="email" />
<span v-if="errors.email" class="error">{{ errors.email }}</span>
</div>
<button type="submit" :disabled="!isFormValid">提交</button>
</form>
</template>
<script>
import { ref, reactive, computed } from 'vue'
export default {
setup() {
const formData = reactive({
username: '',
email: ''
})
const errors = reactive({})
// 验证规则
const validateField = (field, value) => {
switch (field) {
case 'username':
if (!value) return '用户名不能为空'
if (value.length < 3) return '用户名至少3个字符'
break
case 'email':
if (!value) return '邮箱不能为空'
if (!/\S+@\S+\.\S+/.test(value)) return '邮箱格式不正确'
break
}
return ''
}
// 实时验证
const validateForm = () => {
Object.keys(formData).forEach(field => {
errors[field] = validateField(field, formData[field])
})
}
// 计算表单有效性
const isFormValid = computed(() => {
return !Object.values(errors).some(error => error)
})
// 监听表单变化
Object.keys(formData).forEach(field => {
const fieldRef = ref(formData[field])
fieldRef.value = formData[field]
// 使用 watch 监听变化并验证
watch(fieldRef, (newValue) => {
formData[field] = newValue
errors[field] = validateField(field, newValue)
})
})
const handleSubmit = () => {
if (isFormValid.value) {
console.log('表单提交:', formData)
// 处理表单提交逻辑
}
}
return {
formData,
errors,
isFormValid,
handleSubmit
}
}
}
</script>
结语
Vue 3 的 Composition API 为前端开发带来了全新的可能性。通过本文的详细介绍,我们看到了 Composition API 在代码组织、逻辑复用、组件设计等方面的强大优势。
掌握 Composition API 不仅能帮助我们编写更清晰、更可维护的代码,还能显著提升开发效率。在实际项目中,合理运用组合函数、生命周期钩子和响应式 API,能够让我们构建出更加优雅和高效的 Vue 应用。
随着 Vue 生态的不断发展,Composition API 将会成为现代 Vue 开发的标准实践。建议开发者深入学习并熟练掌握这些技术,以适应前端技术的发展趋势,为用户创造更好的产品体验。
记住,好的代码不仅仅是功能的实现,更是可读性、可维护性和可扩展性的完美结合。Composition API 正是帮助我们实现这一目标的重要工具。

评论 (0)