搜索

首页  >  问答  >  正文

Pinia 中的继承/共享操作和吸气剂

我有几个 Pinia 商店,它们应该共享一组操作和 getter,但我不太确定如何有效地实现这一点。

我正在构建一个应用程序,让用户可以管理多种不同的媒体(书籍、电影、电视节目等)。我目前考虑的方式是为每种媒体类型建立一个商店,例如BookStore、MovieStore 等。这些不同商店之间的许多 getter 和操作(例如 countdeleteOne)完全相同。

如何在这里实现 DRY? Pinia 文档中的示例主要集中在其他商店内重用操作和 getter,但我认为这并不能完全解决我直接继承一组 getter 和 setter 的用例。

我在这里尝试的继承方法是反模式吗?

P粉662614213P粉662614213336 天前505

全部回复(2)我来回复

  • P粉420958692

    P粉4209586922023-12-23 12:54:52

    如果您想让某些功能在并非所有商店之间共享,您可以使用可组合方式。

    您可以创建一个单独的可组合函数并将商店实例的一部分传递给其中。

    我在 codesandbox 上做了一个例子为你。

    这里是codesandbox的简短示例:

    common.ts

    import { computed, Ref, ref } from "vue";
    
    export function useCommon(initValue: number) {
        const _value = ref<number>(initValue);
    
        function increment() {
            _value.value++;
        }
    
        function someProcessing() {
            // ... some code here
        }
    
        return {
            counter,
    
            increment,
            someProcessing,
        };
    }

    然后在任何商店中您都可以像这样使用它:

    fooStore.ts

    export const useFooStore = defineStore('foo', () => {
        const state = ref<string>('foo');
    
        const { counter, increment, someProcessing } = useCounter(0);
    
        return {
            state,
    
            counter,
            increment,
            someProcessing,
        }
    }

    通过这种方式,您可以在任何存储或任何组件中组合任何函数、对象等。

    回复
    0
  • P粉449281068

    P粉4492810682023-12-23 00:43:42

    这可以使用插件来实现 docs

    示例电影:

    您有多个商店,每个州都使用共享命名方案:

    • 项目:单个实体项目(单个电影详细信息)
    • collection:项目集合(所有电影的集合)

    每个商店都会有相同的 CRUD 操作,只是 URL 发生变化

    创建插件:

    function BaseStorePlugin () {
        return {
            collection: [],
            item: {},
            getCollection: function (url) {
                api.get(url)
                    .then((response) => {
                        this.collection = response.data;
                    })
                    .catch((error) => {
                        this.handleError(error);
                    });
            },
            getItem: function (url) {
                api.get(url)
                    .then((response) => {
                        this.item = response.data;
                    })
                    .catch((error) => {
                        this.handleError(error);
                    });
            },
            handleError: function (error) {
                window.alert(error);
            },
        };
    }

    给 Pinia 提供插件:

    const pinia = createPinia();
    
    pinia.use(BaseStorePlugin);

    示例 movieStore.js(使用共享操作和状态)

    import { defineStore } from 'pinia';
    import { api } from 'src/boot/axios';
    
    export const useMovieStore = defineStore({
        id: 'movie',
        state: () => ({
            movieSpecificStateObject: {},
        }),
        actions: {
            movieSpecificAction (url) {
                console.log(this.item);
                api.get(url)
                    .then((response) => {
                        // handle response
                    })
                    .catch((error) => {
                        this.handleError(error);
                    });
            },
        },
    });

    组件中的使用示例

    <template>
        <div
            v-for="movie in movieStore.collection"
            :key="movie.id"
        >
            <div>
                {{ movie.name }}
            </div>
        </div>
    </template>
    
    <script setup>
    import { onMounted } from 'vue';
    import { useMovieStore } from 'src/stores/movieStore.js';
    const movieStore = useMovieStore();
    
    onMounted(() => {
        movieStore.readCollection('http://url.com/movies');
    });
    </script>

    编辑:1

    如果您将上下文传递到插件中,您就可以访问商店和传递到其中的选项,从中您可以检查商店 ID,并且仅返回特定商店,如下所示

    function BaseStorePlugin (context) {
      const allowedStores = ['movie', 'album'];
    
      if (allowedStores.includes(context.store.$id)) {
        return {
          collection: [],
          getCollection: function () {
            const fakeCollection = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
            fakeCollection.forEach((item) => {
              this.collection.push({
                id: item,
                name: `name${item}`
              });
            });
          },
        };
      };
    }

    我使用 3 个商店创建了一个非常基本的示例,上面的检查可在 codesandbox 此处< /a>

    回复
    0
  • 取消回复