Vue 3 Composition API 项目实战:从组件设计到状态管理完整指南

深海游鱼姬
深海游鱼姬 2026-02-01T01:16:17+08:00
0 0 1

引言

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 不再将逻辑分散在 datamethodscomputed 等选项中,而是通过组合不同的 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:

  1. 模块化开发:Composition API 让逻辑复用变得更加简单,通过自定义 Hook 可以将通用的业务逻辑抽象出来
  2. 响应式系统优化:合理使用 refreactivecomputed 等 API,确保应用的性能和可维护性
  3. 状态管理最佳实践:结合 Vue 3 的响应式系统和自定义 Hook,构建清晰的状态管理架构
  4. 性能监控与优化:通过性能监控工具和虚拟滚动等技术手段,确保应用的流畅体验

随着 Vue 3 的普及,Composition API 将成为现代 Vue 开发的标准实践。掌握这些技能不仅能够提升开发效率,还能帮助构建更加健壮和可维护的应用程序。在实际项目中,建议根据具体需求选择合适的模式,并持续关注 Vue 生态的发展动态。

通过不断的实践和优化,我们可以充分利用 Composition API 的强大功能,为用户提供更好的用户体验,同时保持代码的清晰度和可维护性。这正是现代前端开发所追求的目标——在保证性能的同时,提供最佳的开发体验。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000