Vue 3 状态管理与跨组件通信完全指南

285 阅读4分钟

Vue 3 状态管理与跨组件通信完全指南

一、核心概念与方案对比

1. 可用的状态管理方案

  • Composition API + Provide/Inject
  • Pinia
  • 自定义响应式存储
  • EventBus
  • Vuex (不推荐在 Vue 3 中使用)

2. 各方案适用场景

方案适用场景优势劣势
Composition API中小型应用,组件树通信轻量,灵活,TypeScript支持好状态分散,不适合大型应用
Pinia中大型应用,全局状态管理类型安全,模块化,开发体验好学习成本,项目体积增加
自定义存储特定场景,轻量级需求完全可控,体积小需要自行实现功能
EventBus跨组件事件通信简单直接难以追踪,耦合度高

二、基于 Composition API 的状态管理

1. 创建响应式状态

// stores/user.ts
import { ref, computed } from 'vue'

export function useUserStore() {
  // 状态
  const user = ref({
    id: '',
    name: '',
    token: ''
  })
  
  // Getters
  const isLoggedIn = computed(() => !!user.value.token)
  
  // Actions
  const login = async (credentials: { username: string; password: string }) => {
    // 模拟登录请求
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    })
    const data = await response.json()
    
    user.value = {
      id: data.id,
      name: data.name,
      token: data.token
    }
  }
  
  const logout = () => {
    user.value = {
      id: '',
      name: '',
      token: ''
    }
  }
  
  return {
    user,
    isLoggedIn,
    login,
    logout
  }
}

2. 全局状态注入

// stores/index.ts
import { provide, inject } from 'vue'
import { useUserStore } from './user'

const StoreSymbol = Symbol('store')

export function provideStore() {
  const store = {
    user: useUserStore()
    // 可以添加其他store
  }
  
  provide(StoreSymbol, store)
  return store
}

export function useStore() {
  const store = inject(StoreSymbol)
  if (!store) throw new Error('Store not provided!')
  return store
}

三、Pinia 状态管理

1. 定义 Store

// stores/counter.ts
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  // 状态
  state: () => ({
    count: 0,
    history: [] as number[]
  }),
  
  // Getters
  getters: {
    doubleCount: (state) => state.count * 2,
    lastChange: (state) => state.history[state.history.length - 1]
  },
  
  // Actions
  actions: {
    increment() {
      this.count++
      this.history.push(this.count)
    },
    
    async fetchInitialCount() {
      const response = await fetch('/api/count')
      const data = await response.json()
      this.count = data.count
    }
  }
})

2. Store 组合

// stores/composite.ts
import { defineStore } from 'pinia'
import { useCounterStore } from './counter'
import { useUserStore } from './user'

export const useCompositeStore = defineStore('composite', {
  state: () => ({
    loading: false
  }),
  
  actions: {
    async initializeApp() {
      this.loading = true
      
      const counter = useCounterStore()
      const user = useUserStore()
      
      try {
        await Promise.all([
          counter.fetchInitialCount(),
          user.checkAuth()
        ])
      } finally {
        this.loading = false
      }
    }
  }
})

四、自定义响应式存储

1. 创建响应式存储类

// utils/reactive-store.ts
import { reactive, readonly } from 'vue'

export class ReactiveStore<T extends object> {
  private state: T
  
  constructor(initialState: T) {
    this.state = reactive(initialState) as T
  }
  
  getState() {
    return readonly(this.state)
  }
  
  patch(partial: Partial<T>) {
    Object.assign(this.state, partial)
  }
  
  set(key: keyof T, value: T[keyof T]) {
    this.state[key] = value
  }
  
  reset(initialState: T) {
    Object.assign(this.state, initialState)
  }
}

2. 使用示例

// stores/theme.ts
interface ThemeState {
  mode: 'light' | 'dark'
  primary: string
  secondary: string
}

export const themeStore = new ReactiveStore<ThemeState>({
  mode: 'light',
  primary: '#1890ff',
  secondary: '#f5f5f5'
})

// 在组件中使用
import { themeStore } from '@/stores/theme'

const theme = themeStore.getState()
const toggleTheme = () => {
  themeStore.set('mode', theme.mode === 'light' ? 'dark' : 'light')
}

五、事件通信系统

1. 实现类型安全的事件总线

// utils/event-bus.ts
type EventCallback<T = any> = (payload: T) => void

class TypedEventBus {
  private events = new Map<string, Set<EventCallback>>()
  
  on<T>(event: string, callback: EventCallback<T>) {
    if (!this.events.has(event)) {
      this.events.set(event, new Set())
    }
    this.events.get(event)!.add(callback)
    
    return () => this.off(event, callback)
  }
  
  off(event: string, callback: EventCallback) {
    this.events.get(event)?.delete(callback)
  }
  
  emit<T>(event: string, payload: T) {
    this.events.get(event)?.forEach(callback => callback(payload))
  }
  
  clear() {
    this.events.clear()
  }
}

export const eventBus = new TypedEventBus()

2. 组件中使用

// 在发送组件中
import { eventBus } from '@/utils/event-bus'

const sendMessage = () => {
  eventBus.emit('chat:message', {
    id: Date.now(),
    text: 'Hello!',
    user: 'John'
  })
}

// 在接收组件中
import { onMounted, onUnmounted } from 'vue'
import { eventBus } from '@/utils/event-bus'

const unsubscribe = eventBus.on('chat:message', (message) => {
  console.log('Received:', message)
})

onUnmounted(() => {
  unsubscribe()
})

六、状态持久化

1. 创建持久化包装器

// utils/persistent-store.ts
interface Storage {
  getItem(key: string): string | null
  setItem(key: string, value: string): void
}

export function createPersistentStore<T extends object>(
  key: string,
  initialState: T,
  storage: Storage = localStorage
) {
  // 读取持久化数据
  const savedState = storage.getItem(key)
  const state = reactive(
    savedState ? JSON.parse(savedState) : initialState
  )
  
  // 监听变化并持久化
  watch(
    () => state,
    (newState) => {
      storage.setItem(key, JSON.stringify(newState))
    },
    { deep: true }
  )
  
  return readonly(state)
}

// 使用示例
const settings = createPersistentStore('app-settings', {
  theme: 'light',
  fontSize: 14,
  notifications: true
})

七、性能优化建议

  1. 状态分割

    • 按功能模块拆分状态
    • 避免大型单一状态树
    • 使用细粒度的响应式
  2. computed 缓存

    • 合理使用计算属性
    • 避免在 getter 中进行复杂计算
  3. 异步操作处理

    • 使用 action 统一处理异步
    • 实现请求去重和缓存
    • 考虑乐观更新
  4. 订阅优化

    • 使用 shallowRef 减少深层响应
    • 及时取消订阅
    • 避免不必要的监听

八、最佳实践建议

  1. 状态设计原则

    • 单一数据源
    • 状态最小化
    • 避免冗余数据
  2. 类型安全

    • 使用 TypeScript
    • 定义清晰的接口
    • 利用类型推导
  3. 错误处理

    • 统一的错误处理策略
    • 状态回滚机制
    • 错误状态管理
  4. 测试友好

    • 状态可序列化
    • 操作可追踪
    • 依赖注入设计

总结

本指南涵盖了 Vue 3 中主要的状态管理方案:

  1. Composition API 的响应式状态
  2. Pinia 的标准化状态管理
  3. 自定义响应式存储
  4. 事件通信系统
  5. 状态持久化方案

选择合适的状态管理方案应考虑:

  • 项目规模和复杂度
  • 团队熟悉度
  • 维护成本
  • 性能需求
  • 开发效率

参考资源