定义存储
在深入核心概念之前,我们需要知道存储是使用 defineStore()
定义的,并且它需要一个唯一的名称,作为第一个参数传递
import { defineStore } from 'pinia'
// You can name the return value of `defineStore()` anything you want,
// but it's best to use the name of the store and surround it with `use`
// and `Store` (e.g. `useUserStore`, `useCartStore`, `useProductStore`)
// the first argument is a unique id of the store across your application
export const useAlertsStore = defineStore('alerts', {
// other options...
})
此名称,也称为id,是必需的,Pinia 使用它将存储连接到开发者工具。将返回的函数命名为use... 是跨可组合函数的约定,使其用法符合习惯用法。
defineStore()
接受两个不同的值作为其第二个参数:一个设置函数或一个选项对象。
选项存储
类似于 Vue 的选项 API,我们还可以传递一个带有 state
、actions
和 getters
属性的选项对象。
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0, name: 'Eduardo' }),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
您可以将 state
视为存储的 data
,将 getters
视为存储的 computed
属性,将 actions
视为 methods
。
选项存储应该让您直观且轻松地上手。
设置存储
还有一种定义存储的语法。类似于 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 }
})
在设置存储中
ref()
成为state
属性computed()
成为getters
function()
成为actions
请注意,您必须在设置存储中返回所有状态属性,以便 Pinia 将它们识别为状态。换句话说,您不能在存储中拥有 私有状态属性。不返回所有状态属性或将它们设为只读将破坏 SSR、开发者工具和其他插件。
设置存储比 选项存储 提供了更多的灵活性,因为您可以在存储中创建观察者,并自由使用任何 可组合函数。但是,请记住,在使用 SSR 时,使用可组合函数会变得更加复杂。
设置存储还可以依赖于全局提供的属性,例如路由器或路由。任何在 App 级别 提供的属性 都可以使用 inject()
从存储中访问,就像在组件中一样
import { inject } from 'vue'
import { useRoute } from 'vue-router'
import { defineStore } from 'pinia'
export const useSearchFilters = defineStore('search-filters', () => {
const route = useRoute()
// this assumes `app.provide('appProvided', 'value')` was called
const appProvided = inject('appProvided')
// ...
return {
// ...
}
})
警告
不要返回诸如 route
或 appProvided
(来自上面的示例)之类的属性,因为它们不属于存储本身,您可以使用 useRoute()
和 inject('appProvided')
在组件中直接访问它们。
我应该选择哪种语法?
与 Vue 的 Composition API 和 Options API 一样,选择您最舒适的一种。两者都有其优缺点。选项存储更容易使用,而设置存储更灵活、更强大。如果您想更深入地了解差异,请查看 Mastering Pinia 中的 选项存储与设置存储章节。
使用存储
我们正在定义一个存储,因为存储只有在组件 <script setup>
中调用 use...Store()
时才会创建(或在 setup()
中,就像所有可组合函数一样)
<script setup>
import { useCounterStore } from '@/stores/counter'
// access the `store` variable anywhere in the component ✨
const store = useCounterStore()
</script>
提示
如果您还没有使用 setup
组件,您仍然可以使用映射助手与 Pinia 一起使用。
您可以定义任意数量的存储,并且您应该在不同的文件中定义每个存储,以充分利用 Pinia(例如,自动允许您的捆绑器代码拆分并提供 TypeScript 推断)。
存储实例化后,您可以直接在存储上访问 state
、getters
和 actions
中定义的任何属性。我们将在接下来的页面中详细介绍这些内容,但自动完成功能将帮助您。
请注意,store
是一个用 reactive
包装的对象,这意味着不需要在获取器之后写 .value
,但是,就像 setup
中的 props
一样,我们不能解构它
<script setup>
import { useCounterStore } from '@/stores/counter'
import { computed } from 'vue'
const store = useCounterStore()
// ❌ This won't work because it breaks reactivity
// it's the same as destructuring from `props`
const { name, doubleCount } = store
name // will always be "Eduardo"
doubleCount // will always be 0
setTimeout(() => {
store.increment()
}, 1000)
// ✅ this one will be reactive
// 💡 but you could also just use `store.doubleCount` directly
const doubleValue = computed(() => store.doubleCount)
</script>
从存储中解构
为了在保持其响应性的同时从存储中提取属性,您需要使用 storeToRefs()
。它将为每个响应式属性创建引用。当您只使用存储中的状态而不调用任何操作时,这很有用。请注意,您可以直接从存储中解构操作,因为它们也绑定到存储本身
<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` and `doubleCount` are reactive refs
// This will also extract refs for properties added by plugins
// but skip any action or non reactive (non ref/reactive) property
const { name, doubleCount } = storeToRefs(store)
// the increment action can just be destructured
const { increment } = store
</script>