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')
const doubleCount = computed(() => count.value * 2)
function increment () {
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,
}
},
})
