前言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。作为 Vue 3 的核心特性之一,Composition API 为开发者提供了更加灵活和强大的组件开发方式。相比传统的 Options API,Composition API 更加符合现代 JavaScript 开发的思维模式,特别是在处理复杂逻辑、组件复用和状态管理方面展现出巨大优势。
本文将深入探讨 Vue 3 Composition API 的核心特性和最佳实践,从基础概念到高级应用,帮助开发者更好地理解和运用这一强大的工具。我们将重点关注 setup 函数、响应式 API、生命周期钩子等关键概念,并分享在实际项目中组件复用、状态管理以及性能优化等方面的实用技巧。
Composition API 核心概念
Setup 函数详解
在 Vue 3 中,setup 函数是 Composition API 的入口点。它接收两个参数:props 和 context,并返回一个对象或函数,该对象中的属性和方法将被暴露给组件模板使用。
import { ref, reactive } from 'vue'
export default {
props: {
title: String,
count: Number
},
setup(props, context) {
// 响应式数据
const message = ref('Hello Vue 3')
const state = reactive({
name: 'John',
age: 25
})
// 方法
const handleClick = () => {
console.log('Button clicked')
}
// 返回给模板使用的数据和方法
return {
message,
state,
handleClick
}
}
}
setup 函数的执行时机是在组件实例创建之前,这意味着在 setup 中无法访问 this,但可以使用响应式 API 创建响应式数据。
响应式 API 深入理解
Vue 3 提供了多种响应式 API 来满足不同的开发需求:
Ref API
import { ref, watch, computed } from 'vue'
export default {
setup() {
// 创建基本响应式引用
const count = ref(0)
const name = ref('Vue')
// 计算属性
const doubledCount = computed(() => count.value * 2)
// 监听器
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// 深度监听
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`Values changed: ${oldCount} -> ${newCount}, ${oldName} -> ${newName}`)
})
return {
count,
name,
doubledCount
}
}
}
Reactive API
import { reactive, watchEffect } from 'vue'
export default {
setup() {
// 创建响应式对象
const state = reactive({
user: {
name: 'John',
age: 25,
address: {
city: 'Beijing'
}
},
items: ['apple', 'banana']
})
// watchEffect 会自动追踪依赖
watchEffect(() => {
console.log(`User: ${state.user.name}, Age: ${state.user.age}`)
})
return {
state
}
}
}
生命周期钩子的使用
Composition API 中的生命周期
在 Composition API 中,Vue 3 提供了与 Options API 对应的生命周期钩子函数:
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate,
onBeforeUnmount
} from 'vue'
export default {
setup() {
// 组件挂载后执行
onMounted(() => {
console.log('Component mounted')
// 可以在这里进行 DOM 操作
})
// 组件更新后执行
onUpdated(() => {
console.log('Component updated')
})
// 组件卸载前执行
onBeforeUnmount(() => {
console.log('Component will unmount')
// 清理定时器等资源
})
return {}
}
}
异步生命周期处理
import { ref, onMounted, onUnmounted } from 'vue'
export default {
setup() {
const data = ref(null)
const timer = ref(null)
const fetchData = async () => {
try {
const response = await fetch('/api/data')
data.value = await response.json()
} catch (error) {
console.error('Failed to fetch data:', error)
}
}
onMounted(() => {
// 组件挂载时获取数据
fetchData()
// 设置定时器
timer.value = setInterval(() => {
console.log('Timer tick')
}, 1000)
})
onUnmounted(() => {
// 组件卸载前清理定时器
if (timer.value) {
clearInterval(timer.value)
}
})
return {
data
}
}
}
组件复用与组合式函数
创建可复用的组合式函数
组合式函数是 Vue 3 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
}
}
// composables/useApi.js
import { ref, watch } 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
}
}
watch(url, fetchData)
return {
data,
loading,
error,
fetchData
}
}
使用组合式函数的组件示例
<template>
<div class="counter">
<h2>Counter: {{ count }}</h2>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="reset">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>
<template>
<div class="api-component">
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else>
<h2>{{ data?.title }}</h2>
<p>{{ data?.content }}</p>
</div>
<button @click="fetchData">Refresh</button>
</div>
</template>
<script>
import { useApi } from '@/composables/useApi'
export default {
setup() {
const { data, loading, error, fetchData } = useApi('/api/posts')
return {
data,
loading,
error,
fetchData
}
}
}
</script>
状态管理最佳实践
简单的状态管理
对于小型应用,可以使用响应式 API 来实现简单的状态管理:
// stores/appStore.js
import { reactive, readonly } from 'vue'
const state = reactive({
user: null,
theme: 'light',
notifications: []
})
export const appStore = {
// 获取状态
getState() {
return readonly(state)
},
// 更新用户信息
setUser(user) {
state.user = user
},
// 切换主题
toggleTheme() {
state.theme = state.theme === 'light' ? 'dark' : 'light'
},
// 添加通知
addNotification(notification) {
state.notifications.push({
id: Date.now(),
...notification,
timestamp: new Date()
})
},
// 移除通知
removeNotification(id) {
const index = state.notifications.findIndex(n => n.id === id)
if (index > -1) {
state.notifications.splice(index, 1)
}
}
}
复杂状态管理模式
对于更复杂的状态管理需求,可以结合 Vue 3 的响应式系统和组合式函数:
// composables/useGlobalState.js
import { reactive, readonly } from 'vue'
export function useGlobalState() {
const state = reactive({
user: null,
permissions: [],
settings: {
language: 'zh-CN',
theme: 'light',
notifications: true
},
loading: false,
error: null
})
const mutations = {
setUser(user) {
state.user = user
},
setPermissions(permissions) {
state.permissions = permissions
},
updateSettings(updates) {
Object.assign(state.settings, updates)
},
setLoading(loading) {
state.loading = loading
},
setError(error) {
state.error = error
}
}
const actions = {
async login(credentials) {
try {
mutations.setLoading(true)
mutations.setError(null)
// 模拟 API 调用
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (!response.ok) {
throw new Error('Login failed')
}
const userData = await response.json()
mutations.setUser(userData)
return userData
} catch (error) {
mutations.setError(error.message)
throw error
} finally {
mutations.setLoading(false)
}
},
logout() {
mutations.setUser(null)
mutations.setPermissions([])
}
}
return {
state: readonly(state),
...mutations,
...actions
}
}
性能优化技巧
计算属性的优化
import { computed, ref } from 'vue'
export default {
setup() {
const items = ref([])
const filterText = ref('')
// 避免不必要的计算
const filteredItems = computed(() => {
if (!filterText.value) return items.value
return items.value.filter(item =>
item.name.toLowerCase().includes(filterText.value.toLowerCase())
)
})
// 缓存计算结果
const expensiveCalculation = computed(() => {
// 模拟耗时操作
let result = 0
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i)
}
return result
})
return {
items,
filterText,
filteredItems,
expensiveCalculation
}
}
}
组件渲染优化
<template>
<div class="optimized-component">
<!-- 使用 v-memo 避免不必要的重渲染 -->
<div v-memo="[item.id, item.name]" v-for="item in items" :key="item.id">
{{ item.name }}
</div>
<!-- 使用 v-once 只渲染一次 -->
<div v-once>
This content will not change
</div>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const items = ref([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
])
return {
items
}
}
}
</script>
防抖和节流优化
import { ref, watch } from 'vue'
// 防抖函数
function debounce(func, wait) {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
// 节流函数
function throttle(func, limit) {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
export default {
setup() {
const searchQuery = ref('')
const results = ref([])
// 防抖搜索
const debouncedSearch = debounce(async (query) => {
if (query.length > 2) {
try {
const response = await fetch(`/api/search?q=${query}`)
results.value = await response.json()
} catch (error) {
console.error('Search error:', error)
}
}
}, 300)
watch(searchQuery, (newQuery) => {
debouncedSearch(newQuery)
})
return {
searchQuery,
results
}
}
}
高级模式与最佳实践
条件渲染和动态组件
<template>
<div class="dynamic-component">
<!-- 动态组件 -->
<component
:is="currentComponent"
v-bind="componentProps"
@update-data="handleUpdateData"
/>
<!-- 条件渲染优化 -->
<div v-if="showAdvancedFeatures">
<AdvancedFeature v-for="feature in features" :key="feature.id" :feature="feature" />
</div>
</div>
</template>
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const currentComponent = ref('BasicComponent')
const showAdvancedFeatures = ref(false)
const features = ref([])
const componentProps = computed(() => ({
title: 'Dynamic Component',
data: features.value
}))
const handleUpdateData = (data) => {
console.log('Data updated:', data)
}
return {
currentComponent,
showAdvancedFeatures,
features,
componentProps,
handleUpdateData
}
}
}
</script>
插槽和组件通信
<template>
<div class="slot-component">
<!-- 默认插槽 -->
<slot />
<!-- 命名插槽 -->
<slot name="header" :user="user" />
<!-- 作用域插槽 -->
<slot
name="footer"
:items="items"
:loading="loading"
/>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
props: ['title'],
setup(props) {
const user = ref({ name: 'John', role: 'admin' })
const items = ref([])
const loading = ref(false)
return {
user,
items,
loading
}
}
}
</script>
错误处理和调试
import { ref, onErrorCaptured, onMounted } from 'vue'
export default {
setup() {
const error = ref(null)
const data = ref(null)
// 捕获子组件错误
onErrorCaptured((err, instance, info) => {
console.error('Error captured:', err, info)
error.value = err.message
return false // 阻止错误继续冒泡
})
const fetchData = async () => {
try {
const response = await fetch('/api/data')
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
data.value = await response.json()
} catch (err) {
console.error('Fetch error:', err)
error.value = err.message
throw err // 重新抛出错误以便上层处理
}
}
onMounted(() => {
fetchData().catch(err => {
console.error('Component mount error:', err)
})
})
return {
data,
error
}
}
}
总结与展望
Vue 3 Composition API 的引入为前端开发带来了革命性的变化。通过本文的深入探讨,我们可以看到:
-
灵活性提升:Composition API 提供了更灵活的代码组织方式,使得复杂的组件逻辑更容易管理和复用。
-
更好的逻辑复用:组合式函数让开发者能够轻松地将通用逻辑封装成可复用的模块。
-
性能优化空间:通过合理的计算属性、防抖节流等技巧,可以显著提升应用性能。
-
开发体验改善:更符合现代 JavaScript 开发习惯的 API 设计,降低了学习成本和开发难度。
随着 Vue 3 生态系统的不断完善,Composition API 将在更多场景中发挥重要作用。未来的开发中,我们应当充分利用这些特性来构建更加高效、可维护的前端应用。同时,也要注意避免过度复杂化,保持代码的简洁性和可读性。
通过持续实践和探索,开发者可以更好地掌握 Composition API 的精髓,在实际项目中发挥其最大价值,为用户提供更好的产品体验。

评论 (0)