Vue 3 Composition API实战指南:组件复用与状态管理的最佳方案

LongBronze
LongBronze 2026-02-12T10:07:10+08:00
0 0 0

引言

Vue 3 的发布带来了革命性的变化,其中最引人注目的就是 Composition API 的引入。这一新特性不仅解决了 Vue 2 中选项式 API 的诸多限制,还为开发者提供了更灵活、更强大的组件开发方式。本文将深入探讨 Composition API 的核心特性,演示如何构建可复用的组合式函数、实现复杂的状态管理,并通过实际案例展示如何优化组件结构,提升开发效率和代码质量。

Composition API 核心概念

什么是 Composition API

Composition API 是 Vue 3 中引入的一种新的组件开发方式,它允许我们以函数的形式组织和复用组件逻辑。与 Vue 2 中的选项式 API(Options API)不同,Composition API 将组件的逻辑按功能进行组织,而不是按选项类型分组。

核心函数介绍

Composition API 提供了一系列核心函数,这些函数是构建复杂应用的基础:

import { 
  ref, 
  reactive, 
  computed, 
  watch, 
  watchEffect,
  onMounted, 
  onUpdated, 
  onUnmounted,
  provide, 
  inject 
} from 'vue'

这些函数提供了从响应式数据到生命周期钩子的完整功能支持。

响应式数据管理

Ref 与 Reactive 的区别

在 Composition API 中,refreactive 是两种主要的响应式数据创建方式:

import { ref, reactive } from 'vue'

// 使用 ref 创建响应式数据
const count = ref(0)
const name = ref('Vue')

// 使用 reactive 创建响应式对象
const state = reactive({
  count: 0,
  name: 'Vue',
  user: {
    age: 20,
    email: 'vue@example.com'
  }
})

// 访问值时的区别
console.log(count.value) // 0
console.log(state.count) // 0

深层响应式与浅层响应式

import { shallowRef, triggerRef } from 'vue'

// 浅层响应式
const shallow = shallowRef({
  nested: { count: 0 }
})

// 修改嵌套对象不会触发更新
shallow.value.nested.count = 1 // 不会触发更新

// 手动触发更新
triggerRef(shallow)

组合式函数(Composable Functions)

构建可复用的逻辑

组合式函数是 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
  }
}

// 在组件中使用
import { useCounter } from '@/composables/useCounter'

export default {
  setup() {
    const { count, increment, decrement, doubleCount } = useCounter(10)
    
    return {
      count,
      increment,
      decrement,
      doubleCount
    }
  }
}

复杂状态管理示例

// composables/useApi.js
import { ref, computed } 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)
      data.value = await response.json()
    } catch (err) {
      error.value = err.message
    } finally {
      loading.value = false
    }
  }
  
  const hasData = computed(() => !!data.value)
  const hasError = computed(() => !!error.value)
  
  return {
    data,
    loading,
    error,
    fetchData,
    hasData,
    hasError
  }
}

// 在组件中使用
import { useApi } from '@/composables/useApi'

export default {
  setup() {
    const { data, loading, error, fetchData, hasData } = useApi('/api/users')
    
    fetchData()
    
    return {
      data,
      loading,
      error,
      hasData
    }
  }
}

生命周期钩子

组件生命周期管理

Composition API 提供了与 Vue 2 相同的生命周期钩子,但以函数形式提供:

import { 
  onMounted, 
  onUpdated, 
  onUnmounted, 
  onBeforeMount,
  onBeforeUpdate,
  onBeforeUnmount
} from 'vue'

export default {
  setup() {
    // 组件挂载时执行
    onMounted(() => {
      console.log('组件已挂载')
      // 初始化定时器等操作
    })
    
    // 组件更新时执行
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    // 组件卸载前执行
    onBeforeUnmount(() => {
      console.log('组件即将卸载')
      // 清理定时器等操作
    })
    
    // 组件卸载时执行
    onUnmounted(() => {
      console.log('组件已卸载')
    })
    
    return {}
  }
}

异步数据获取最佳实践

// composables/useAsyncData.js
import { ref, watch } from 'vue'

export function useAsyncData(fetcher, options = {}) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  const refresh = ref(0)
  
  const fetchData = async (params = {}) => {
    loading.value = true
    error.value = null
    
    try {
      const result = await fetcher(params)
      data.value = result
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  // 监听刷新计数器
  watch(refresh, () => {
    fetchData()
  })
  
  // 自动获取数据
  fetchData()
  
  return {
    data,
    loading,
    error,
    refresh: () => refresh.value++,
    fetchData
  }
}

状态管理与依赖注入

Provide/Inject 机制

Provide/Inject 是 Vue 中实现跨层级组件通信的重要机制:

// 父组件
import { provide, reactive } from 'vue'

export default {
  setup() {
    const globalState = reactive({
      user: null,
      theme: 'light',
      language: 'zh-CN'
    })
    
    provide('globalState', globalState)
    provide('updateTheme', (theme) => {
      globalState.theme = theme
    })
    
    return {
      globalState
    }
  }
}

// 子组件
import { inject } from 'vue'

export default {
  setup() {
    const globalState = inject('globalState')
    const updateTheme = inject('updateTheme')
    
    const switchTheme = () => {
      const newTheme = globalState.theme === 'light' ? 'dark' : 'light'
      updateTheme(newTheme)
    }
    
    return {
      globalState,
      switchTheme
    }
  }
}

复杂状态管理示例

// composables/useGlobalStore.js
import { reactive, readonly } from 'vue'

// 创建全局状态
const state = reactive({
  user: null,
  theme: 'light',
  language: 'zh-CN',
  notifications: []
})

// 提供只读状态访问
const readonlyState = readonly(state)

// 提供状态更新方法
const mutations = {
  setUser(user) {
    state.user = user
  },
  
  setTheme(theme) {
    state.theme = theme
  },
  
  addNotification(notification) {
    state.notifications.push(notification)
  },
  
  removeNotification(id) {
    const index = state.notifications.findIndex(n => n.id === id)
    if (index > -1) {
      state.notifications.splice(index, 1)
    }
  }
}

// 提供全局状态访问
export function useGlobalStore() {
  return {
    state: readonlyState,
    ...mutations
  }
}

组件复用策略

逻辑复用的最佳实践

// composables/useForm.js
import { ref, reactive } from 'vue'

export function useForm(initialData = {}) {
  const formData = reactive({ ...initialData })
  const errors = ref({})
  const isSubmitting = ref(false)
  
  const validate = (rules = {}) => {
    const newErrors = {}
    
    Object.keys(rules).forEach(field => {
      const rule = rules[field]
      const value = formData[field]
      
      if (rule.required && !value) {
        newErrors[field] = `${field} 是必填项`
      }
      
      if (rule.minLength && value && value.length < rule.minLength) {
        newErrors[field] = `${field} 长度不能少于 ${rule.minLength} 位`
      }
    })
    
    errors.value = newErrors
    return Object.keys(newErrors).length === 0
  }
  
  const submit = async (submitHandler) => {
    if (!validate()) return false
    
    isSubmitting.value = true
    
    try {
      const result = await submitHandler(formData)
      return result
    } catch (error) {
      console.error('提交失败:', error)
      return false
    } finally {
      isSubmitting.value = false
    }
  }
  
  const reset = () => {
    Object.keys(formData).forEach(key => {
      formData[key] = initialData[key] || ''
    })
    errors.value = {}
  }
  
  return {
    formData,
    errors,
    isSubmitting,
    validate,
    submit,
    reset
  }
}

// 在组件中使用
import { useForm } from '@/composables/useForm'

export default {
  setup() {
    const { 
      formData, 
      errors, 
      isSubmitting, 
      validate, 
      submit,
      reset 
    } = useForm({
      username: '',
      email: '',
      password: ''
    })
    
    const rules = {
      username: { required: true, minLength: 3 },
      email: { required: true, minLength: 5 },
      password: { required: true, minLength: 6 }
    }
    
    const handleSubmit = async (data) => {
      // 实际的提交逻辑
      console.log('提交数据:', data)
      return { success: true }
    }
    
    const handleSave = async () => {
      const isValid = validate(rules)
      if (isValid) {
        const result = await submit(handleSubmit)
        if (result.success) {
          // 处理成功逻辑
          console.log('保存成功')
        }
      }
    }
    
    return {
      formData,
      errors,
      isSubmitting,
      handleSave,
      reset
    }
  }
}

组件组合模式

// composables/usePagination.js
import { ref, computed } from 'vue'

export function usePagination(total = 0, pageSize = 10) {
  const currentPage = ref(1)
  const pageSizeRef = ref(pageSize)
  
  const totalPage = computed(() => {
    return Math.ceil(total / pageSizeRef.value)
  })
  
  const hasNext = computed(() => {
    return currentPage.value < totalPage.value
  })
  
  const hasPrev = computed(() => {
    return currentPage.value > 1
  })
  
  const goToPage = (page) => {
    if (page >= 1 && page <= totalPage.value) {
      currentPage.value = page
    }
  }
  
  const next = () => {
    if (hasNext.value) {
      currentPage.value++
    }
  }
  
  const prev = () => {
    if (hasPrev.value) {
      currentPage.value--
    }
  }
  
  const setPageSize = (size) => {
    pageSizeRef.value = size
    currentPage.value = 1
  }
  
  return {
    currentPage,
    pageSize: pageSizeRef,
    totalPage,
    hasNext,
    hasPrev,
    goToPage,
    next,
    prev,
    setPageSize
  }
}

// 在组件中使用
import { usePagination } from '@/composables/usePagination'

export default {
  setup() {
    const { 
      currentPage, 
      totalPage, 
      hasNext, 
      hasPrev,
      next, 
      prev,
      goToPage 
    } = usePagination(100, 10)
    
    return {
      currentPage,
      totalPage,
      hasNext,
      hasPrev,
      next,
      prev,
      goToPage
    }
  }
}

性能优化与最佳实践

计算属性与监听器优化

import { 
  computed, 
  watch, 
  watchEffect,
  shallowRef,
  markRaw
} from 'vue'

// 使用 watchEffect 优化监听
export default {
  setup() {
    const count = ref(0)
    const name = ref('Vue')
    
    // watchEffect 会自动追踪依赖
    watchEffect(() => {
      console.log(`count: ${count.value}, name: ${name.value}`)
    })
    
    // 使用 shallowRef 优化复杂对象
    const complexData = shallowRef({
      items: [],
      metadata: {}
    })
    
    // 只监听特定属性
    watch(() => count.value, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })
    
    return {
      count,
      name
    }
  }
}

避免重复计算

// composables/useMemo.js
import { computed, ref } from 'vue'

export function useMemo(computation, dependencies) {
  const cache = ref(null)
  const hasCache = ref(false)
  
  const result = computed(() => {
    if (!hasCache.value) {
      cache.value = computation()
      hasCache.value = true
    }
    return cache.value
  })
  
  // 依赖变化时清除缓存
  watch(dependencies, () => {
    hasCache.value = false
  })
  
  return result
}

// 使用示例
export default {
  setup() {
    const data = ref([])
    const filter = ref('')
    
    const filteredData = useMemo(() => {
      return data.value.filter(item => 
        item.name.toLowerCase().includes(filter.value.toLowerCase())
      )
    }, [data, filter])
    
    return {
      filteredData
    }
  }
}

实际应用案例

电商购物车实现

// composables/useShoppingCart.js
import { ref, computed, watch } from 'vue'

export function useShoppingCart() {
  const items = ref([])
  
  // 添加商品到购物车
  const addToCart = (product, quantity = 1) => {
    const existingItem = items.value.find(item => item.id === product.id)
    
    if (existingItem) {
      existingItem.quantity += quantity
    } else {
      items.value.push({
        ...product,
        quantity
      })
    }
  }
  
  // 移除商品
  const removeFromCart = (productId) => {
    items.value = items.value.filter(item => item.id !== productId)
  }
  
  // 更新商品数量
  const updateQuantity = (productId, quantity) => {
    const item = items.value.find(item => item.id === productId)
    if (item) {
      item.quantity = Math.max(0, quantity)
      if (item.quantity === 0) {
        removeFromCart(productId)
      }
    }
  }
  
  // 计算总价
  const totalPrice = computed(() => {
    return items.value.reduce((total, item) => {
      return total + (item.price * item.quantity)
    }, 0)
  })
  
  // 计算商品总数
  const totalItems = computed(() => {
    return items.value.reduce((total, item) => total + item.quantity, 0)
  })
  
  // 清空购物车
  const clearCart = () => {
    items.value = []
  }
  
  // 保存到本地存储
  watch(items, (newItems) => {
    localStorage.setItem('shoppingCart', JSON.stringify(newItems))
  }, { deep: true })
  
  // 从本地存储恢复
  const restoreFromStorage = () => {
    const saved = localStorage.getItem('shoppingCart')
    if (saved) {
      items.value = JSON.parse(saved)
    }
  }
  
  return {
    items,
    addToCart,
    removeFromCart,
    updateQuantity,
    totalPrice,
    totalItems,
    clearCart,
    restoreFromStorage
  }
}

// 在组件中使用
import { useShoppingCart } from '@/composables/useShoppingCart'

export default {
  setup() {
    const { 
      items, 
      addToCart, 
      removeFromCart, 
      updateQuantity,
      totalPrice,
      totalItems,
      clearCart,
      restoreFromStorage
    } = useShoppingCart()
    
    // 恢复购物车状态
    restoreFromStorage()
    
    return {
      items,
      addToCart,
      removeFromCart,
      updateQuantity,
      totalPrice,
      totalItems,
      clearCart
    }
  }
}

用户认证系统

// composables/useAuth.js
import { ref, computed } from 'vue'

export function useAuth() {
  const user = ref(null)
  const token = ref(null)
  const loading = ref(false)
  const error = ref(null)
  
  // 登录
  const login = async (credentials) => {
    loading.value = true
    error.value = null
    
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(credentials)
      })
      
      const data = await response.json()
      
      if (response.ok) {
        user.value = data.user
        token.value = data.token
        localStorage.setItem('authToken', data.token)
        return { success: true }
      } else {
        throw new Error(data.message || '登录失败')
      }
    } catch (err) {
      error.value = err.message
      return { success: false, error: err.message }
    } finally {
      loading.value = false
    }
  }
  
  // 注销
  const logout = () => {
    user.value = null
    token.value = null
    localStorage.removeItem('authToken')
  }
  
  // 检查认证状态
  const checkAuth = () => {
    const savedToken = localStorage.getItem('authToken')
    if (savedToken) {
      token.value = savedToken
      // 这里可以添加验证 token 的逻辑
      user.value = { name: 'User' } // 模拟用户数据
    }
  }
  
  // 检查用户是否已登录
  const isAuthenticated = computed(() => !!user.value)
  
  // 获取用户信息
  const userInfo = computed(() => user.value)
  
  // 获取访问令牌
  const accessToken = computed(() => token.value)
  
  // 初始化认证状态
  checkAuth()
  
  return {
    user: userInfo,
    token: accessToken,
    loading,
    error,
    login,
    logout,
    isAuthenticated,
    checkAuth
  }
}

总结

Vue 3 的 Composition API 为前端开发带来了革命性的变化,它不仅解决了 Vue 2 中选项式 API 的诸多限制,还提供了更灵活、更强大的组件开发方式。通过合理使用组合式函数、响应式数据管理、生命周期钩子和状态管理机制,我们可以构建出更加模块化、可复用和易于维护的组件。

在实际开发中,建议遵循以下最佳实践:

  1. 合理组织逻辑:将相关的功能逻辑封装成组合式函数,提高代码复用性
  2. 优化响应式数据:根据数据特点选择合适的响应式创建方式
  3. 合理使用生命周期:在适当的时机执行初始化和清理操作
  4. 性能优化:合理使用计算属性、监听器和缓存机制
  5. 状态管理:对于复杂应用,可以结合 provide/inject 或状态管理库实现全局状态管理

通过深入理解和掌握 Composition API 的核心特性,开发者可以构建出更加高效、可维护的 Vue 应用程序,提升开发效率和代码质量。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000