引言
Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。相比传统的 Options API,Composition API 提供了更灵活、更强大的组件开发方式,特别适合处理复杂的业务逻辑和状态管理。本文将通过一个完整的项目案例,深入探讨 Vue 3 Composition API 的实际应用,涵盖从组件设计到状态管理的各个方面。
Vue 3 Composition API 核心概念
什么是 Composition API
Composition API 是 Vue 3 中引入的一种新的组件开发方式,它允许开发者以函数的形式组织和重用逻辑代码。与传统的 Options API 不同,Composition API 不再将逻辑分散在 data、methods、computed 等选项中,而是通过组合不同的 API 函数来构建组件。
核心 API 函数
import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue'
// 响应式数据
const count = ref(0)
const user = reactive({ name: 'John', age: 25 })
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 监听器
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// 生命周期钩子
onMounted(() => {
console.log('组件已挂载')
})
实际项目案例:任务管理应用
项目需求分析
我们将构建一个任务管理应用,包含以下功能:
- 任务列表展示
- 添加、编辑、删除任务
- 任务状态切换(待办、进行中、已完成)
- 任务筛选和搜索
- 数据持久化
组件结构设计
src/
├── components/
│ ├── TaskList.vue
│ ├── TaskItem.vue
│ ├── TaskForm.vue
│ └── TaskFilters.vue
├── composables/
│ ├── useTasks.js
│ ├── useFilter.js
│ └── useStorage.js
└── stores/
└── taskStore.js
组件重构与响应式数据处理
传统 Options API 与 Composition API 对比
让我们先看一个传统的任务组件实现:
// 传统 Options API 实现
export default {
data() {
return {
tasks: [],
newTask: '',
filter: 'all'
}
},
computed: {
filteredTasks() {
// 筛选逻辑
},
taskCount() {
// 统计逻辑
}
},
methods: {
addTask() {
// 添加任务逻辑
},
deleteTask(id) {
// 删除任务逻辑
}
},
mounted() {
this.loadTasks()
}
}
使用 Composition API 的重构版本:
<!-- TaskList.vue -->
<template>
<div class="task-list">
<h2>任务管理</h2>
<!-- 任务表单 -->
<TaskForm @add-task="handleAddTask" />
<!-- 筛选器 -->
<TaskFilters
:filter="filter"
@filter-change="handleFilterChange"
/>
<!-- 任务列表 -->
<div class="tasks">
<TaskItem
v-for="task in filteredTasks"
:key="task.id"
:task="task"
@update-task="handleUpdateTask"
@delete-task="handleDeleteTask"
/>
</div>
<!-- 统计信息 -->
<div class="stats">
<p>总计: {{ totalTasks }} | 待办: {{ pendingTasks }}</p>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import TaskForm from './TaskForm.vue'
import TaskItem from './TaskItem.vue'
import TaskFilters from './TaskFilters.vue'
// 响应式数据
const tasks = ref([])
const newTask = ref('')
const filter = ref('all')
// 计算属性
const filteredTasks = computed(() => {
switch (filter.value) {
case 'active':
return tasks.value.filter(task => !task.completed)
case 'completed':
return tasks.value.filter(task => task.completed)
default:
return tasks.value
}
})
const totalTasks = computed(() => tasks.value.length)
const pendingTasks = computed(() =>
tasks.value.filter(task => !task.completed).length
)
// 方法
const handleAddTask = (taskData) => {
const task = {
id: Date.now(),
...taskData,
completed: false,
createdAt: new Date()
}
tasks.value.push(task)
}
const handleUpdateTask = (updatedTask) => {
const index = tasks.value.findIndex(t => t.id === updatedTask.id)
if (index !== -1) {
tasks.value[index] = updatedTask
}
}
const handleDeleteTask = (taskId) => {
tasks.value = tasks.value.filter(task => task.id !== taskId)
}
const handleFilterChange = (newFilter) => {
filter.value = newFilter
}
// 生命周期钩子
onMounted(() => {
loadTasks()
})
// 加载任务数据
const loadTasks = () => {
const savedTasks = localStorage.getItem('tasks')
if (savedTasks) {
tasks.value = JSON.parse(savedTasks)
}
}
</script>
自定义 Hook 开发实践
创建任务管理 Hook
// composables/useTasks.js
import { ref, computed } from 'vue'
import { useStorage } from './useStorage'
export function useTasks() {
const { getItem, setItem } = useStorage()
// 响应式状态
const tasks = ref([])
const loading = ref(false)
const error = ref(null)
// 计算属性
const filteredTasks = computed(() => {
return tasks.value.filter(task => {
// 这里可以添加筛选逻辑
return true
})
})
const totalTasks = computed(() => tasks.value.length)
const completedTasks = computed(() =>
tasks.value.filter(task => task.completed).length
)
// 方法
const loadTasks = async () => {
loading.value = true
error.value = null
try {
const savedTasks = getItem('tasks')
tasks.value = savedTasks || []
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
const addTask = async (taskData) => {
const newTask = {
id: Date.now(),
...taskData,
completed: false,
createdAt: new Date()
}
tasks.value.push(newTask)
await saveTasks()
return newTask
}
const updateTask = async (updatedTask) => {
const index = tasks.value.findIndex(t => t.id === updatedTask.id)
if (index !== -1) {
tasks.value[index] = updatedTask
await saveTasks()
}
}
const deleteTask = async (taskId) => {
tasks.value = tasks.value.filter(task => task.id !== taskId)
await saveTasks()
}
const toggleTask = async (taskId) => {
const index = tasks.value.findIndex(t => t.id === taskId)
if (index !== -1) {
tasks.value[index].completed = !tasks.value[index].completed
await saveTasks()
}
}
const saveTasks = () => {
return setItem('tasks', tasks.value)
}
const clearCompleted = async () => {
tasks.value = tasks.value.filter(task => !task.completed)
await saveTasks()
}
// 初始化
loadTasks()
return {
tasks,
filteredTasks,
loading,
error,
totalTasks,
completedTasks,
addTask,
updateTask,
deleteTask,
toggleTask,
clearCompleted
}
}
创建存储管理 Hook
// composables/useStorage.js
import { ref, watch } from 'vue'
export function useStorage() {
const storage = localStorage
const getItem = (key) => {
try {
const item = storage.getItem(key)
return item ? JSON.parse(item) : null
} catch (error) {
console.error(`Error getting item ${key}:`, error)
return null
}
}
const setItem = (key, value) => {
try {
storage.setItem(key, JSON.stringify(value))
return true
} catch (error) {
console.error(`Error setting item ${key}:`, error)
return false
}
}
const removeItem = (key) => {
try {
storage.removeItem(key)
return true
} catch (error) {
console.error(`Error removing item ${key}:`, error)
return false
}
}
// 监听存储变化
const watchStorage = (key, callback) => {
const handler = (e) => {
if (e.key === key) {
callback(e.newValue)
}
}
window.addEventListener('storage', handler)
return () => {
window.removeEventListener('storage', handler)
}
}
return {
getItem,
setItem,
removeItem,
watchStorage
}
}
状态管理与数据流优化
使用 Vue 3 的响应式系统
// stores/taskStore.js
import { reactive, readonly } from 'vue'
const state = reactive({
tasks: [],
filter: 'all',
loading: false,
error: null
})
// getters
const getters = {
filteredTasks: () => {
switch (state.filter) {
case 'active':
return state.tasks.filter(task => !task.completed)
case 'completed':
return state.tasks.filter(task => task.completed)
default:
return state.tasks
}
},
totalTasks: () => state.tasks.length,
completedTasks: () =>
state.tasks.filter(task => task.completed).length,
pendingTasks: () =>
state.tasks.filter(task => !task.completed).length
}
// actions
const actions = {
async loadTasks() {
state.loading = true
try {
const response = await fetch('/api/tasks')
state.tasks = await response.json()
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
},
addTask(taskData) {
const newTask = {
id: Date.now(),
...taskData,
completed: false,
createdAt: new Date()
}
state.tasks.push(newTask)
},
updateTask(updatedTask) {
const index = state.tasks.findIndex(t => t.id === updatedTask.id)
if (index !== -1) {
state.tasks[index] = updatedTask
}
},
deleteTask(taskId) {
state.tasks = state.tasks.filter(task => task.id !== taskId)
},
setFilter(filter) {
state.filter = filter
}
}
// mutations
const mutations = {
SET_LOADING(loading) {
state.loading = loading
},
SET_ERROR(error) {
state.error = error
}
}
export default {
state: readonly(state),
getters,
actions,
mutations
}
在组件中使用状态管理
<!-- TaskList.vue -->
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useTasks } from '../composables/useTasks.js'
import TaskForm from './TaskForm.vue'
import TaskItem from './TaskItem.vue'
import TaskFilters from './TaskFilters.vue'
const {
tasks,
filteredTasks,
loading,
error,
addTask,
updateTask,
deleteTask,
toggleTask,
clearCompleted
} = useTasks()
// 组件本地状态
const showForm = ref(false)
const handleAddTask = async (taskData) => {
try {
await addTask(taskData)
showForm.value = false
} catch (err) {
console.error('添加任务失败:', err)
}
}
const handleUpdateTask = async (updatedTask) => {
try {
await updateTask(updatedTask)
} catch (err) {
console.error('更新任务失败:', err)
}
}
const handleDeleteTask = async (taskId) => {
try {
await deleteTask(taskId)
} catch (err) {
console.error('删除任务失败:', err)
}
}
onMounted(() => {
// 组件挂载时的初始化逻辑
})
</script>
高级响应式数据处理
响应式对象的深层嵌套处理
import { reactive, watch, watchEffect } from 'vue'
// 复杂嵌套对象的响应式处理
const user = reactive({
profile: {
personal: {
name: 'John',
age: 25,
address: {
street: '123 Main St',
city: 'New York'
}
},
preferences: {
theme: 'light',
notifications: true
}
},
settings: {
privacy: {
publicProfile: true,
showEmail: false
}
}
})
// 深度监听嵌套属性
watch(() => user.profile.personal.name, (newName, oldName) => {
console.log(`姓名从 ${oldName} 变为 ${newName}`)
})
// 使用 watchEffect 自动追踪依赖
watchEffect(() => {
console.log(`用户: ${user.profile.personal.name}, 城市: ${user.profile.personal.address.city}`)
})
// 避免响应式丢失的处理方式
const reactiveData = reactive({
items: []
})
// 正确的数组操作方式
const addItem = (item) => {
// 使用 Vue 的响应式 API 确保响应性
reactiveData.items.push(item)
}
// 或者使用计算属性来确保数据一致性
const computedItems = computed(() => {
return reactiveData.items.filter(item => item.active)
})
性能优化策略
import { ref, computed, watch, shallowRef, triggerRef } from 'vue'
// 使用 shallowRef 处理大型对象
const largeObject = shallowRef({
// 大型数据结构
})
// 只监听引用变化,不深度追踪内部属性
watch(largeObject, (newVal) => {
console.log('largeObject changed:', newVal)
})
// 使用 computed 的缓存特性
const expensiveCalculation = computed(() => {
// 耗时的计算逻辑
return someExpensiveOperation()
})
// 条件监听优化
const shouldWatch = ref(false)
watch(shouldWatch, (newValue) => {
if (newValue) {
// 启动监听
} else {
// 停止监听
}
})
// 使用 watchEffect 的清理函数
const cleanupWatch = watchEffect((onCleanup) => {
const timer = setTimeout(() => {
console.log('定时器执行')
}, 1000)
// 清理函数
onCleanup(() => {
clearTimeout(timer)
})
})
组件通信与数据流管理
父子组件通信优化
<!-- ParentComponent.vue -->
<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentData = ref('来自父组件的数据')
const parentMethod = () => {
console.log('父组件方法被调用')
}
// 提供数据给子组件
provide('parentData', parentData)
provide('parentMethod', parentMethod)
const handleChildEvent = (data) => {
console.log('接收到子组件事件:', data)
}
</script>
<template>
<div>
<h2>父组件</h2>
<p>{{ parentData }}</p>
<ChildComponent @child-event="handleChildEvent" />
</div>
</template>
<!-- ChildComponent.vue -->
<script setup>
import { inject } from 'vue'
// 注入父组件提供的数据
const parentData = inject('parentData')
const parentMethod = inject('parentMethod')
const emit = defineEmits(['child-event'])
const handleClick = () => {
// 调用父组件方法
parentMethod()
// 向父组件发送事件
emit('child-event', '来自子组件的数据')
}
</script>
<template>
<div>
<h3>子组件</h3>
<p>{{ parentData }}</p>
<button @click="handleClick">点击测试</button>
</div>
</template>
全局状态管理实践
// stores/globalStore.js
import { reactive, readonly } from 'vue'
const state = reactive({
user: null,
theme: 'light',
language: 'zh-CN',
notifications: []
})
const getters = {
isAuthenticated: () => !!state.user,
isDarkMode: () => state.theme === 'dark'
}
const actions = {
setUser(user) {
state.user = user
},
setTheme(theme) {
state.theme = theme
},
addNotification(notification) {
state.notifications.push({
id: Date.now(),
...notification,
timestamp: new Date()
})
},
removeNotification(id) {
state.notifications = state.notifications.filter(n => n.id !== id)
}
}
// 创建全局状态管理器
export const useGlobalStore = () => {
return {
state: readonly(state),
getters,
actions
}
}
错误处理与调试最佳实践
统一错误处理机制
import { ref, computed } from 'vue'
export function useErrorHandler() {
const error = ref(null)
const loading = ref(false)
const handleError = (error) => {
console.error('应用错误:', error)
error.value = error.message || '未知错误'
// 可以在这里添加错误上报逻辑
if (process.env.NODE_ENV === 'production') {
// 生产环境错误上报
reportErrorToService(error)
}
}
const handleAsyncOperation = async (asyncFn, ...args) => {
loading.value = true
error.value = null
try {
const result = await asyncFn(...args)
return result
} catch (err) {
handleError(err)
throw err
} finally {
loading.value = false
}
}
const resetError = () => {
error.value = null
}
return {
error,
loading,
handleAsyncOperation,
resetError
}
}
// 使用示例
const { error, loading, handleAsyncOperation } = useErrorHandler()
const fetchUserData = async (userId) => {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
return response.json()
}
const loadUser = async () => {
try {
const userData = await handleAsyncOperation(fetchUserData, 123)
console.log('用户数据:', userData)
} catch (err) {
console.error('加载用户失败:', err)
}
}
开发者工具集成
// utils/debug.js
export function createDebugger(name) {
const debug = (message, data = null) => {
if (process.env.NODE_ENV === 'development') {
console.log(`[DEBUG ${name}]`, message, data)
}
}
const warn = (message, data = null) => {
if (process.env.NODE_ENV === 'development') {
console.warn(`[WARN ${name}]`, message, data)
}
}
const error = (message, data = null) => {
if (process.env.NODE_ENV === 'development') {
console.error(`[ERROR ${name}]`, message, data)
}
}
return { debug, warn, error }
}
// 在组件中使用
const logger = createDebugger('TaskList')
export default {
setup() {
const tasks = ref([])
const addTask = (task) => {
logger.debug('添加任务', task)
tasks.value.push(task)
logger.debug('任务列表更新', tasks.value)
}
return {
tasks,
addTask
}
}
}
性能监控与优化
响应式数据的性能监控
import { ref, computed, watch } from 'vue'
export function usePerformanceMonitor() {
const performanceData = ref({
renderTime: 0,
updateCount: 0,
lastUpdate: null
})
const monitorRender = (fn) => {
const start = performance.now()
const result = fn()
const end = performance.now()
performanceData.value.renderTime = end - start
performanceData.value.updateCount++
performanceData.value.lastUpdate = new Date()
return result
}
const getPerformanceStats = () => {
return {
...performanceData.value,
averageRenderTime: performanceData.value.renderTime /
Math.max(performanceData.value.updateCount, 1)
}
}
return {
monitorRender,
getPerformanceStats
}
}
// 在组件中使用
const { monitorRender, getPerformanceStats } = usePerformanceMonitor()
const tasks = ref([])
const filteredTasks = computed(() => {
// 监控计算属性的性能
return monitorRender(() => {
return tasks.value.filter(task => !task.completed)
})
})
虚拟滚动优化
<!-- VirtualScroll.vue -->
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const props = defineProps({
items: { type: Array, default: () => [] },
itemHeight: { type: Number, default: 50 }
})
const containerRef = ref(null)
const visibleStart = ref(0)
const visibleEnd = ref(0)
const scrollTop = ref(0)
const visibleItems = computed(() => {
const startIndex = Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - 5)
const endIndex = Math.min(
props.items.length,
startIndex + Math.ceil(containerRef.value?.clientHeight / props.itemHeight) + 10
)
return props.items.slice(startIndex, endIndex)
})
const handleScroll = () => {
if (containerRef.value) {
scrollTop.value = containerRef.value.scrollTop
}
}
onMounted(() => {
containerRef.value?.addEventListener('scroll', handleScroll)
})
onUnmounted(() => {
containerRef.value?.removeEventListener('scroll', handleScroll)
})
</script>
<template>
<div
ref="containerRef"
class="virtual-scroll-container"
:style="{ height: '400px', overflow: 'auto' }"
>
<div :style="{ height: items.length * itemHeight + 'px' }">
<div
v-for="(item, index) in visibleItems"
:key="item.id"
:style="{
position: 'absolute',
top: (visibleStart + index) * itemHeight + 'px',
height: itemHeight + 'px'
}"
>
<slot :item="item" :index="visibleStart + index"></slot>
</div>
</div>
</div>
</template>
总结
通过本文的实战案例,我们深入探讨了 Vue 3 Composition API 的各个方面。从基础的响应式数据处理到复杂的自定义 Hook 开发,从状态管理到性能优化,Composition API 为 Vue 开发带来了前所未有的灵活性和强大功能。
Key Takeaways:
- 模块化开发:Composition API 让逻辑复用变得更加简单,通过自定义 Hook 可以将通用的业务逻辑抽象出来
- 响应式系统优化:合理使用
ref、reactive、computed等 API,确保应用的性能和可维护性 - 状态管理最佳实践:结合 Vue 3 的响应式系统和自定义 Hook,构建清晰的状态管理架构
- 性能监控与优化:通过性能监控工具和虚拟滚动等技术手段,确保应用的流畅体验
随着 Vue 3 的普及,Composition API 将成为现代 Vue 开发的标准实践。掌握这些技能不仅能够提升开发效率,还能帮助构建更加健壮和可维护的应用程序。在实际项目中,建议根据具体需求选择合适的模式,并持续关注 Vue 生态的发展动态。
通过不断的实践和优化,我们可以充分利用 Composition API 的强大功能,为用户提供更好的用户体验,同时保持代码的清晰度和可维护性。这正是现代前端开发所追求的目标——在保证性能的同时,提供最佳的开发体验。

评论 (0)