Vue 3 Composition API 架构设计模式:从组件通信到状态管理的完整指南

Judy356
Judy356 2026-02-27T11:20:01+08:00
0 0 0

引言

Vue 3 的发布带来了 Composition API 的重大革新,这一特性不仅改变了我们编写组件的方式,更深刻地影响了前端应用的架构设计模式。Composition API 通过将逻辑组织成可重用的函数,为大型应用提供了更灵活、更可维护的开发体验。本文将深入探讨 Composition API 的架构设计理念,详细阐述组件间通信模式、响应式数据管理、状态持久化等关键技术,并通过实际项目案例演示如何构建可维护、可扩展的现代化前端应用架构。

Vue 3 Composition API 核心概念

什么是 Composition API

Composition API 是 Vue 3 中引入的一种新的组件逻辑组织方式。与传统的 Options API 不同,Composition API 允许我们按照逻辑功能来组织代码,而不是按照选项类型(data、methods、computed 等)来分组。这种组织方式使得代码更加灵活,更易于维护和重用。

Composition API 的优势

  1. 更好的逻辑复用:通过组合函数(composables)实现逻辑复用,避免了 Mixins 的命名空间冲突问题
  2. 更清晰的代码组织:按照功能逻辑组织代码,而不是按照选项类型
  3. 更灵活的组件设计:可以更自由地组合和重用逻辑
  4. 更好的 TypeScript 支持:类型推断更加准确和直观

基础 API 详解

import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    // 响应式数据
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 30
    })
    
    // 计算属性
    const doubleCount = computed(() => count.value * 2)
    
    // 监听器
    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })
    
    // 生命周期钩子
    onMounted(() => {
      console.log('Component mounted')
    })
    
    // 返回给模板使用的数据和方法
    return {
      count,
      user,
      doubleCount,
      increment: () => count.value++
    }
  }
}

组件间通信模式

1. Props 和 Emit 模式

Props 和 Emit 是 Vue 中最基本的组件通信方式,适用于父子组件通信。

// 父组件
<template>
  <child-component 
    :user="currentUser" 
    @user-updated="handleUserUpdate"
    @submit="handleSubmit"
  />
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const currentUser = ref({
  name: 'John',
  email: 'john@example.com'
})

const handleUserUpdate = (updatedUser) => {
  currentUser.value = updatedUser
}

const handleSubmit = (formData) => {
  console.log('Form submitted:', formData)
}
</script>
// 子组件
<template>
  <div class="user-form">
    <input v-model="user.name" placeholder="Name" />
    <input v-model="user.email" placeholder="Email" />
    <button @click="submitForm">Submit</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const props = defineProps({
  user: {
    type: Object,
    required: true
  }
})

const emit = defineEmits(['userUpdated', 'submit'])

const user = ref({ ...props.user })

// 监听 props 变化
watch(() => props.user, (newUser) => {
  user.value = { ...newUser }
})

const submitForm = () => {
  emit('userUpdated', user.value)
  emit('submit', user.value)
}
</script>

2. Provide/Inject 模式

Provide/Inject 模式适用于跨层级组件通信,避免了 props 的层层传递。

// 父组件
<template>
  <div class="app">
    <child-component />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const theme = ref('dark')
const user = ref({ name: 'John', role: 'admin' })

provide('appTheme', theme)
provide('currentUser', user)
provide('updateUser', (newUser) => {
  user.value = newUser
})
</script>
// 子组件
<template>
  <div :class="theme">
    <h1>{{ user.name }}</h1>
    <button @click="changeTheme">Toggle Theme</button>
  </div>
</template>

<script setup>
import { inject, ref } from 'vue'

const theme = inject('appTheme')
const user = inject('currentUser')
const updateUser = inject('updateUser')

const changeTheme = () => {
  theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
</script>

3. 全局状态管理模式

对于复杂的跨组件通信需求,可以使用全局状态管理。

// stores/userStore.js
import { reactive, readonly } from 'vue'

const state = reactive({
  currentUser: null,
  isLoggedIn: false,
  permissions: []
})

const setUser = (user) => {
  state.currentUser = user
  state.isLoggedIn = !!user
  state.permissions = user?.permissions || []
}

const logout = () => {
  state.currentUser = null
  state.isLoggedIn = false
  state.permissions = []
}

const getUser = () => readonly(state.currentUser)

export const useUserStore = () => {
  return {
    state: readonly(state),
    setUser,
    logout,
    getUser
  }
}
// 在组件中使用
<script setup>
import { useUserStore } from '@/stores/userStore'

const { state, setUser, logout } = useUserStore()

const handleLogin = (userData) => {
  setUser(userData)
}

const handleLogout = () => {
  logout()
}
</script>

响应式数据管理

1. Ref 和 Reactive 的使用

Ref 和 Reactive 是 Vue 3 响应式系统的核心,它们提供了不同的响应式数据处理方式。

import { ref, reactive, toRefs, toRaw } from 'vue'

// Ref 用于基本数据类型
const count = ref(0)
const name = ref('John')
const isActive = ref(true)

// Reactive 用于对象和数组
const user = reactive({
  name: 'John',
  age: 30,
  hobbies: ['reading', 'coding'],
  address: {
    city: 'New York',
    country: 'USA'
  }
})

// 使用 toRefs 转换响应式对象
const useUserStore = () => {
  const user = reactive({
    name: 'John',
    age: 30,
    email: 'john@example.com'
  })
  
  // 将响应式对象转换为 ref
  return {
    ...toRefs(user)
  }
}

2. 计算属性和监听器

计算属性和监听器是响应式数据管理的重要组成部分。

import { ref, computed, watch, watchEffect } 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}`
    })
    
    const isAdult = computed(() => {
      return age.value >= 18
    })
    
    // 带 getter 和 setter 的计算属性
    const displayName = computed({
      get: () => `${firstName.value} ${lastName.value}`,
      set: (value) => {
        const names = value.split(' ')
        firstName.value = names[0]
        lastName.value = names[1]
      }
    })
    
    // 监听器
    watch(firstName, (newVal, oldVal) => {
      console.log(`First name changed from ${oldVal} to ${newVal}`)
    })
    
    // 监听多个值
    watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
      console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)
    })
    
    // watchEffect
    const watchEffectExample = () => {
      const count = ref(0)
      
      watchEffect(() => {
        console.log(`Count is: ${count.value}`)
      })
      
      // 会自动追踪依赖
      count.value++ // 输出: Count is: 1
    }
    
    return {
      firstName,
      lastName,
      age,
      fullName,
      isAdult,
      displayName
    }
  }
}

3. 深度响应式和浅响应式

Vue 3 提供了不同级别的响应式处理能力。

import { ref, reactive, shallowRef, shallowReactive, readonly } from 'vue'

// 深度响应式
const deepObj = reactive({
  nested: {
    value: 1
  }
})

// 浅响应式 - 只响应顶层属性
const shallowObj = shallowReactive({
  nested: {
    value: 1
  }
})

// 浅 ref - 只响应顶层属性
const shallowRefObj = shallowRef({
  nested: {
    value: 1
  }
})

// 只读响应式
const readOnlyData = readonly({
  name: 'John',
  age: 30
})

状态持久化策略

1. localStorage 持久化

// 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
}

// 使用示例
<script setup>
import { useLocalStorage } from '@/composables/useLocalStorage'

const userPreferences = useLocalStorage('userPreferences', {
  theme: 'light',
  language: 'en',
  notifications: true
})

const toggleTheme = () => {
  userPreferences.value.theme = 
    userPreferences.value.theme === 'light' ? 'dark' : 'light'
}
</script>

2. SessionStorage 持久化

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

export function useSessionStorage(key, defaultValue) {
  const storedValue = sessionStorage.getItem(key)
  const value = ref(storedValue ? JSON.parse(storedValue) : defaultValue)
  
  watch(value, (newValue) => {
    sessionStorage.setItem(key, JSON.stringify(newValue))
  }, { deep: true })
  
  return value
}

// 用于存储临时状态
const tempData = useSessionStorage('tempData', {})

3. IndexedDB 持久化

对于复杂的数据持久化需求,可以使用 IndexedDB。

// composables/useIndexedDB.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useIndexedDB(dbName, storeName) {
  const db = ref(null)
  const isReady = ref(false)
  
  const initDB = async () => {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, 1)
      
      request.onerror = () => reject(request.error)
      request.onsuccess = () => {
        db.value = request.result
        isReady.value = true
        resolve(request.result)
      }
      
      request.onupgradeneeded = (event) => {
        const db = event.target.result
        if (!db.objectStoreNames.contains(storeName)) {
          db.createObjectStore(storeName, { keyPath: 'id' })
        }
      }
    })
  }
  
  const saveData = async (data) => {
    if (!db.value || !isReady.value) {
      await initDB()
    }
    
    const transaction = db.value.transaction([storeName], 'readwrite')
    const store = transaction.objectStore(storeName)
    const request = store.add({ ...data, id: Date.now() })
    
    return new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result)
      request.onerror = () => reject(request.error)
    })
  }
  
  const getData = async (id) => {
    if (!db.value || !isReady.value) {
      await initDB()
    }
    
    const transaction = db.value.transaction([storeName], 'readonly')
    const store = transaction.objectStore(storeName)
    const request = store.get(id)
    
    return new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result)
      request.onerror = () => reject(request.error)
    })
  }
  
  onMounted(() => {
    initDB()
  })
  
  return {
    saveData,
    getData,
    isReady
  }
}

组合函数最佳实践

1. 组合函数命名规范

// 好的命名规范
export function useUserStore() { /* ... */ }
export function useApiRequest() { /* ... */ }
export function useLocalStorage() { /* ... */ }
export function useWindowResize() { /* ... */ }

// 避免的命名
export function userStore() { /* ... */ }
export function api() { /* ... */ }
export function localStorage() { /* ... */ }

2. 组合函数参数设计

// 带默认参数的组合函数
export function useFetch(url, options = {}) {
  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, options)
      data.value = await response.json()
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  return {
    data,
    loading,
    error,
    fetchData
  }
}

// 使用示例
const { data, loading, error, fetchData } = useFetch('/api/users')
fetchData()

3. 组合函数返回值设计

// 返回可复用的组合函数
export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  const reset = () => count.value = initialValue
  
  // 返回响应式数据和方法
  return {
    count,
    increment,
    decrement,
    reset
  }
}

// 使用示例
const { count, increment, decrement } = useCounter(10)

实际项目架构案例

1. 电商应用架构

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

export function useCart() {
  const items = useLocalStorage('cartItems', [])
  
  const cartCount = computed(() => items.value.length)
  
  const cartTotal = computed(() => {
    return items.value.reduce((total, item) => {
      return total + (item.price * item.quantity)
    }, 0)
  })
  
  const addToCart = (product) => {
    const existingItem = items.value.find(item => item.id === product.id)
    
    if (existingItem) {
      existingItem.quantity += 1
    } else {
      items.value.push({
        ...product,
        quantity: 1
      })
    }
  }
  
  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 = quantity
    }
  }
  
  return {
    items,
    cartCount,
    cartTotal,
    addToCart,
    removeFromCart,
    updateQuantity
  }
}

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

export function useAuth() {
  const user = useLocalStorage('currentUser', null)
  const token = useLocalStorage('authToken', null)
  
  const isLoggedIn = computed(() => !!user.value && !!token.value)
  
  const login = (userData, tokenData) => {
    user.value = userData
    token.value = tokenData
  }
  
  const logout = () => {
    user.value = null
    token.value = null
  }
  
  const updateProfile = (profileData) => {
    if (user.value) {
      user.value = { ...user.value, ...profileData }
    }
  }
  
  return {
    user,
    token,
    isLoggedIn,
    login,
    logout,
    updateProfile
  }
}

2. 数据管理架构

// services/api.js
import { ref } from 'vue'

const API_BASE_URL = 'https://api.example.com'

export class ApiService {
  static async get(endpoint) {
    const response = await fetch(`${API_BASE_URL}${endpoint}`)
    return response.json()
  }
  
  static async post(endpoint, data) {
    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    return response.json()
  }
  
  static async put(endpoint, data) {
    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    return response.json()
  }
  
  static async delete(endpoint) {
    const response = await fetch(`${API_BASE_URL}${endpoint}`, {
      method: 'DELETE'
    })
    return response.json()
  }
}

// composables/useResource.js
import { ref, computed } from 'vue'
import { ApiService } from '@/services/api'

export function useResource(resourceName, id = null) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  
  const resource = computed(() => data.value)
  
  const fetch = async () => {
    loading.value = true
    error.value = null
    
    try {
      if (id) {
        data.value = await ApiService.get(`/${resourceName}/${id}`)
      } else {
        data.value = await ApiService.get(`/${resourceName}`)
      }
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  const create = async (payload) => {
    loading.value = true
    error.value = null
    
    try {
      const result = await ApiService.post(`/${resourceName}`, payload)
      data.value = result
      return result
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  const update = async (payload) => {
    loading.value = true
    error.value = null
    
    try {
      const result = await ApiService.put(`/${resourceName}/${id}`, payload)
      data.value = result
      return result
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  const remove = async () => {
    loading.value = true
    error.value = null
    
    try {
      await ApiService.delete(`/${resourceName}/${id}`)
      data.value = null
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  return {
    resource,
    loading,
    error,
    fetch,
    create,
    update,
    remove
  }
}

性能优化策略

1. 计算属性缓存

// 优化前
const expensiveCalculation = computed(() => {
  // 复杂计算
  return data.value.items.reduce((sum, item) => {
    return sum + item.price * item.quantity
  }, 0)
})

// 优化后 - 使用缓存
const expensiveCalculation = computed({
  get: () => {
    // 复杂计算
    return data.value.items.reduce((sum, item) => {
      return sum + item.price * item.quantity
    }, 0)
  },
  set: (value) => {
    // 设置逻辑
  }
})

2. 组件懒加载

// 使用动态导入实现懒加载
import { defineAsyncComponent } from 'vue'

const AsyncComponent = defineAsyncComponent(() => 
  import('./components/HeavyComponent.vue')
)

// 在模板中使用
<template>
  <async-component v-if="showComponent" />
</template>

<script setup>
import { ref } from 'vue'

const showComponent = ref(false)

const toggleComponent = () => {
  showComponent.value = !showComponent.value
}
</script>

3. 事件处理优化

// 防抖和节流优化
import { ref } from 'vue'

export function useDebounce(fn, delay = 300) {
  let timeoutId
  return (...args) => {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => fn.apply(this, args), delay)
  }
}

export function useThrottle(fn, limit = 300) {
  let inThrottle
  return (...args) => {
    if (!inThrottle) {
      fn.apply(this, args)
      inThrottle = true
      setTimeout(() => inThrottle = false, limit)
    }
  }
}

// 使用示例
const debouncedSearch = useDebounce(async (query) => {
  // 搜索逻辑
}, 500)

const throttledScroll = useThrottle(() => {
  // 滚动处理逻辑
}, 100)

总结

Vue 3 Composition API 为前端应用架构带来了革命性的变化。通过合理的组件通信模式设计、响应式数据管理策略以及状态持久化方案,我们可以构建出更加可维护、可扩展的现代化前端应用。

在实际项目中,我们应当:

  1. 合理选择通信方式:根据组件层级关系选择合适的通信模式
  2. 善用组合函数:将可复用的逻辑封装成组合函数
  3. 注重性能优化:合理使用计算属性缓存、事件处理优化等技术
  4. 数据持久化策略:根据数据重要性和复杂度选择合适的持久化方案
  5. 遵循最佳实践:保持代码的一致性和可读性

通过深入理解和灵活运用 Composition API 的设计理念,我们可以构建出更加优雅、高效的前端应用架构,为用户提供更好的体验,同时提升开发效率和代码质量。随着 Vue 生态的不断发展,Composition API 将继续发挥重要作用,推动前端开发技术的进步。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000