我需要在 Vue 组件中多次导入 Pinia store 属性吗?

Pri*_*aly 4 javascript vue.js vuejs3 pinia

我正在开发我的第一个 Vue 项目。我习惯了 React 和 vanilla js,但在这里只是了解 Vue 中的一些概念。

特别是,从 Pinia 存储导入状态和操作属性,并且似乎必须在单个 Vue 组件中多次导入这些属性(我不需要在 React 中执行此操作)。

在此示例中,我导入一个简单的计数值和一个增量函数,并尝试在几个不同的地方使用它们:

<script setup>
// I import everything initially in setup, which works fine,
// and these props (currentCount and incrementCount)
// can be used in my template:
import { storeToRefs } from 'pinia';
import { useStore } from '@/stores/store';
const { currentCount } = storeToRefs(useStore());
const { incrementCount } = useStore();
</script>

<template>
  <main>
    Current count: {{ currentCount }}
    <button @click="incrementCount">Increment</button>
  </main>
</template>

<script>
// I can't use store values from setup here.
// This doesn't work:
// console.log(currentCount);

// I also can't import store values here.
// I get the following error:
// "getActivePinia was called with no active Pinia"
// const { currentCount } = storeToRefs(useStore());

export default {
  mounted() {
    // I have to import store values here for them to work:
    const { currentCount } = storeToRefs(useStore());
    console.log(currentCount);
  },
  watch: {
    // weirdly, this reference to watching "currentCount" works:
    currentCount() {
      // I also have to import store values here for them to work:
      const { currentCount } = storeToRefs(useStore());
      console.log(currentCount);
    },
  },
};
</script>
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,如果我想在我的模板、挂载和观察程序中使用存储值(我将使用 React 的 useEffect 钩子),我必须总共导入存储道具 3 次。

这是正确/正常的吗?有没有更简单的方法来实现我正在做的事情,我只导入一次道具?我想确保我没有错过任何事情,也没有以不寻常的方式做某事。

感谢您的任何帮助和建议!

a.h*_*.g. 8

Pinia 的设计考虑了 Composition API。
因此它的预期用途是在setup()函数内部,您只需导入一次。

要在函数之外使用它setup(),有两个主要途径:

  • 在组件内部,您只需从中返回它setup(),它就可以在任何钩子/方法/吸气剂中使用。作为this.store或传播:
import { useStore } from '@/store'
import { toRefs } from 'vue'
            // or from '@vue/composition-api' in Vue2

export default {
  setup: () => ({ ...toRefs(useStore()) })
}
/* this makes every state prop, getter or action directly available 
   on current component instance. In your case, `this.currentCount`.
   Obviously, you can also make the entire store available as `this.someStore`:

  setup: () => ({ someStore: useSomeStore() })
  // now you can use `this.someStore` anywhere 
 */
Run Code Online (Sandbox Code Playgroud)
  • 更通用的方法是从 导出pinia实例(由 返回createPinia()),main.(js|ts)将其导入到需要存储的位置,然后调用useStore()将 pinia 实例作为参数传递。
    这可以在任何地方完成,甚至可以在组件之外完成。
    通用示例:
import { pinia } from 'main.js'
import { useSomeStore } from '@/store'

const someStore = useSomeStore(pinia);
Run Code Online (Sandbox Code Playgroud)

我可能还应该mapState提到pinia. 它允许您仅选择向当前实例公开的几个键。例子:

import { mapState } from 'pinia'
// ...
   computed: {
    ...mapState(useSomeStore, [ 'currentCount'])
  }
// Now `this.currentCount` is available
Run Code Online (Sandbox Code Playgroud)

注意:mapState这个名字很奇怪,因为它允许你访问的不仅仅是stateprops(还有gettersactions)。它的命名是mapState为了匹配来自 的类似助手vuex


更通用的方法是使用 Vue2 中的插件注册 API 将您的商店添加为全局:

import { useSomeStore } from '@/store';
import { createPinia } from 'pinia';

const pinia = createPinia();

const someStorePlugin = {
  install(Vue, options) {
    Vue.prototype.someStore = useSomeStore(options.pinia)
  }
};

Vue.use(someStorePlugin, { pinia });

new Vue({ pinia });
Run Code Online (Sandbox Code Playgroud)

之后,Vue 实例的每个组件都将可用this.someStore,而无需导入它。

注意:我还没有测试过在全局变量中添加商店(我绝对建议不要这样做 - 你应该避免全局变量),但我希望它能够工作。