defineStore
我们得知道 Store 是用defineStore()
定义的,它的第一个参数要求是一个独一无二的名字:
import { defineStore } from 'pinia' // 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`) // 第一个参数是你的应用中 Store 的唯一 ID export const useStore = defineStore('main', { // 其他配置... })
这个名字,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为use...
是一个符合组合式函数风格的约定。
defineStore()
的第二个参数可接受两类值::Setup函数或Options对象。
Option 式
与 Vue 的 Options API(选项式 API)类似,我们也可以传入一个带有state
、actions
和getters
属性的Options对象。
export const useCounterStore = defineStore('counter', {state : () => ({ count: 0, name: 'Eduardo' }),getters : { doubleCount: (state) => state.count * 2, },actions : { increment() { this.count++ }, }, })
你可以认为state
是 Store 的数据(data
),getters
是 Store 的计算属性(computed
),而actions
则是 Store 的方法(methods
)。
为方便上手使用,Option Store 应尽可能直观简单。
Setup 式
还有另一种可能的语法来定义 Store。与 Vue Composition API 的Setup函数类似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => { const count = ref(0) const name = ref('Eduardo') constdoubleCount = computed(() => count.value * 2) functionincrement () { count.value++ } return { count, name,doubleCount ,increment } })
在Setup式中:ref()成为state
属性,computed()变成getters
,function()变成actions
。
- Setup式 Store 比Options式 Store,带来更多的灵活性,因为您可以在 Store 中,创建观察者并自由使用任何可组合的。但是,请记住,使用任何可组合,会得更复杂的 SSR。
- 与 Vue 的 Composition API 和 Option API一样,选择你觉得最舒服的一个。如果您不确定,请先尝试 Option 式 Store。
使用Store
虽然我们前面定义了一个 store(名称为类似use…Store的格式),但在组件setup()
中,调用useStore()
之前,store 实例是不会被创建的:
import { useCounterStore } from '@/stores/counter' export default {setup() { const store = useCounterStore() return { // 为了能在模板中使用它,你可以返回整个 Store 实例 store, } }, }
你可以定义任意多的 store,但为了让使用 pinia 的益处最大化(比如允许构建工具自动进行代码分割以及 TypeScript 推断),你应该在不同的文件中去定义 store。
如果你还不会使用 setup 组件,你也可以通过映射辅助函数来使用 Pinia。
一旦 store 被实例化,你可以直接访问在 store 的 state、getters 和 actions 中定义的任何属性。我们将在后续章节继续了解这些细节,目前自动补全将帮助你使用相关属性。
⚠注意,如果这 Store 是一个用
包裹的对象,这意味着不需要在getter
之后写.value
。就像
中的props一样,我们不能解构它,因为会丧失响应性:
export default defineComponent({ setup() { const store = useCounterStore() // ❌ 这将无法生效,因为它破坏了响应性 // 这与从 `props` 中解构是一样的 const { name, doubleCount } = store name // "Eduardo" doubleCount // 0 setTimeout(() => { store.increment() }, 1000) return { // 始终是 "Eduardo" name, // will always be 0 doubleCount, // will also always be 0 doubleNumber: store.doubleCount, // ✅ 个将是响应式的 doubleValue: computed(() => store.doubleCount), } }, })
为了从 Store 中提取属性,同时保持其响应性,您需要使用storeToRefs()
,它将为每个响应性属性创建引用。当你只使用 Store 的状态而不调用任何action时,它会非常有用。
⚠注意,您可以直接从 Store 中解构action,因为它们也绑定到 Store 本身:
import { storeToRefs } from 'pinia' export default defineComponent({ setup() { const store = useCounterStore() // `name` and `doubleCount` 都是响应式 refs // 这也将为由插件添加的属性创建 refs //同时会跳过任何 action 或非响应式(非 ref/响应式)属性 const { name, doubleCount } = storeToRefs(store) // 名为 increment 的 action 可以直接提取 const {increment } = store return { name, doubleCount, increment, } }, })