You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pinia中如何实现Store共享action与getter?是否为反模式?

实现方案

针对多个Pinia Store存在重复getter和action的场景,推荐两种符合DRY原则的实现方式:

1. 基础Store工厂函数

把通用的state、getter和action抽离到工厂函数中,具体媒体Store通过扩展工厂返回值复用逻辑,同时添加自身特有的内容。

基础工厂示例

// stores/baseMedia.js
export function createBaseMediaStore() {
  return {
    state: () => ({
      items: []
    }),
    getters: {
      count: (state) => state.items.length,
      getById: (state) => (id) => state.items.find(item => item.id === id)
    },
    actions: {
      deleteOne(id) {
        this.items = this.items.filter(item => item.id !== id)
      },
      addOne(item) {
        this.items.push(item)
      }
    }
  }
}

具体媒体Store示例(以书籍为例)

// stores/book.js
import { defineStore } from 'pinia'
import { createBaseMediaStore } from './baseMedia'

export const useBookStore = defineStore('books', {
  // 扩展通用逻辑
  ...createBaseMediaStore(),
  // 覆盖state并添加书籍特有属性
  state: () => ({
    ...createBaseMediaStore().state(),
    readingProgress: {}
  }),
  // 合并通用getter与书籍特有getter
  getters: {
    ...createBaseMediaStore().getters,
    finishedCount: (state) => state.items.filter(book => book.isFinished).length
  },
  // 合并通用action与书籍特有action
  actions: {
    ...createBaseMediaStore().actions,
    updateReadingProgress(bookId, progress) {
      this.readingProgress[bookId] = progress
    }
  }
})

2. 组合式API风格的Composable

如果项目采用Pinia的组合式API写法,可将通用逻辑封装成可复用的composable,在具体Store中导入使用。

通用逻辑Composable示例

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

export function useMediaStoreLogic() {
  const items = ref([])
  
  const count = computed(() => items.value.length)
  const getById = (id) => items.value.find(item => item.id === id)
  
  function deleteOne(id) {
    items.value = items.value.filter(item => item.id !== id)
  }
  
  function addOne(item) {
    items.value.push(item)
  }
  
  return {
    items,
    count,
    getById,
    deleteOne,
    addOne
  }
}

具体媒体Store示例(组合式写法)

// stores/book.js
import { defineStore } from 'pinia'
import { useMediaStoreLogic } from '../composables/useMediaStore'
import { ref, computed } from 'vue'

export const useBookStore = defineStore('books', () => {
  // 复用通用逻辑
  const { items, count, getById, deleteOne, addOne } = useMediaStoreLogic()
  
  // 书籍特有状态
  const readingProgress = ref({})
  
  // 书籍特有计算属性
  const finishedCount = computed(() => items.value.filter(book => book.isFinished).length)
  
  // 书籍特有方法
  function updateReadingProgress(bookId, progress) {
    readingProgress.value[bookId] = progress
  }
  
  return {
    items,
    count,
    getById,
    deleteOne,
    addOne,
    readingProgress,
    finishedCount,
    updateReadingProgress
  }
})
是否属于反模式?

这种复用通用逻辑的思路不是反模式,反而契合Pinia的设计理念——Pinia本身就鼓励通过组合、复用的方式减少重复代码。

需要注意的边界情况:

  • 避免过度抽象:如果某类媒体的业务逻辑与通用逻辑差异极大,强行复用会增加代码复杂度,此时单独编写Store更合适。
  • 保持职责单一:每个媒体Store只处理对应类型的数据,通用逻辑仅抽离公共部分,确保代码清晰可维护。

内容的提问来源于stack exchange,提问作者Daniel Richter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:30:40