Vuex 与 Composition API 的深度整合实践

引言

随着 Vue 3 的 Composition API 逐渐成为主流开发模式,传统的 Vuex 使用方式也需要与时俱进。本文将深入探讨如何在组合式 API 环境中高效使用 Vuex,包括核心的 useStore 方法、状态访问模式以及如何封装可复用的 Vuex 组合逻辑。

一、使用 useStore 组合式函数

基本用法

在 Vue 3 的 setup 函数中,我们可以使用 useStore 来获取 store 实例:

import { useStore } from 'vuex'

export default {
  setup() {
    const store = useStore()
    
    return {
      store
    }
  }
}

类型支持(TypeScript)

import { useStore } from 'vuex'
import type { Store } from 'vuex'
import type { State } from '@/store'

export default {
  setup() {
    const store: Store<State> = useStore()
    
    return {
      store
    }
  }
}

实践建议

  • 在大型项目中,推荐为 store 创建全局类型定义
  • useStore 调用封装在自定义 hook 中以实现统一管理

二、在 setup 中访问 State 和 Getters

访问 State 的几种方式

  1. 直接访问(不推荐,缺乏响应性):

    const count = store.state.count
  2. 使用 computed 包装(推荐):

    import { computed } from 'vue'
    
    const count = computed(() => store.state.count)
  3. 解构保持响应性

    import { toRefs } from 'vue'
    
    const { count } = toRefs(store.state)

访问 Getters

const doubleCount = computed(() => store.getters.doubleCount)

实践建议

  • 优先使用 computed 包装,确保响应性
  • 对于频繁访问的状态,考虑使用 toRefs 解构
  • 避免在 setup 中直接修改 state,始终通过 mutations

三、封装自定义 Vuex 组合逻辑

基础封装示例

// useCounterStore.js
import { computed } from 'vue'
import { useStore } from 'vuex'

export function useCounterStore() {
  const store = useStore()
  
  const count = computed(() => store.state.count)
  const doubleCount = computed(() => store.getters.doubleCount)
  
  const increment = () => store.commit('increment')
  const decrement = () => store.commit('decrement')
  
  return {
    count,
    doubleCount,
    increment,
    decrement
  }
}

带参数的封装

// useUserStore.js
export function useUserStore(userId) {
  const store = useStore()
  
  const user = computed(() => 
    store.getters.getUserById(userId)
  )
  
  const fetchUser = () => 
    store.dispatch('fetchUser', userId)
  
  return {
    user,
    fetchUser
  }
}

组合多个 store 模块

// useCombinedStore.js
export function useCombinedStore() {
  const { count, increment } = useCounterStore()
  const { user, fetchUser } = useUserStore(123)
  
  return {
    count,
    increment,
    user,
    fetchUser
  }
}

实践建议

  • 按业务领域而非技术功能组织组合函数
  • 为复杂模块提供清晰的文档注释
  • 考虑将组合函数放在 src/composables 目录下

四、高级模式与最佳实践

自动注销 Action

import { onUnmounted } from 'vue'

export function useAutoCancelActions() {
  const store = useStore()
  let actions = []
  
  const dispatch = (type, payload) => {
    const promise = store.dispatch(type, payload)
    actions.push(promise)
    return promise
  }
  
  onUnmounted(() => {
    actions.forEach(promise => {
      // 根据你的需求取消 action
    })
    actions = []
  })
  
  return { dispatch }
}

状态变更监听

import { watch } from 'vue'

export function useStoreWatcher(getter, callback) {
  const store = useStore()
  
  watch(
    () => getter(store),
    (newVal, oldVal) => {
      callback(newVal, oldVal, store)
    },
    { deep: true }
  )
}

性能优化技巧

图1

图示说明:通过组合函数创建中间层,可以减少组件与 store 的直接耦合,同时便于集中优化。

最佳实践清单

  1. 避免在组合函数中直接修改 state
  2. 为频繁使用的 getters 添加缓存
  3. 将相关操作组合到同一函数中
  4. 提供清晰的错误处理机制
  5. 考虑添加调试日志选项

五、常见问题解决方案

响应性丢失问题

问题:解构 store 属性时失去响应性

解决方案

// 错误方式
const { count } = store.state

// 正确方式
const count = computed(() => store.state.count)
// 或
const { count } = toRefs(store.state)

循环依赖问题

问题:组合函数之间相互引用导致循环依赖

解决方案

  • 重构代码层次,创建基础组合函数
  • 使用动态导入解决循环引用
// 在需要时动态导入
const useUserStore = () => import('./useUserStore')

结语

Vuex 与 Composition API 的结合为状态管理带来了新的可能性。通过合理封装组合函数,我们既能享受 Vuex 集中式状态管理的优势,又能利用 Composition API 的灵活性和可组合性。记住,良好的封装应该隐藏实现细节,提供简洁的 API,并保持类型安全(如果使用 TypeScript)。

评论已关闭