跳至内容

定义存储

在深入核心概念之前,我们需要知道存储是使用 defineStore() 定义的,并且它需要一个唯一的名称,作为第一个参数传递

js
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,我们还可以传递一个带有 stateactionsgetters 属性的选项对象。

js
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 函数,我们可以传入一个函数,该函数定义响应式属性和方法,并返回一个包含我们要公开的属性和方法的对象。

js
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() 从存储中访问,就像在组件中一样

ts
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 {
    // ...
  }
})

警告

不要返回诸如 routeappProvided(来自上面的示例)之类的属性,因为它们不属于存储本身,您可以使用 useRoute()inject('appProvided') 在组件中直接访问它们。

我应该选择哪种语法?

Vue 的 Composition API 和 Options API 一样,选择您最舒适的一种。两者都有其优缺点。选项存储更容易使用,而设置存储更灵活、更强大。如果您想更深入地了解差异,请查看 Mastering Pinia 中的 选项存储与设置存储章节

使用存储

我们正在定义一个存储,因为存储只有在组件 <script setup> 中调用 use...Store() 时才会创建(或在 setup() 中,就像所有可组合函数一样

vue
<script setup>
import { useCounterStore } from '@/stores/counter'

// access the `store` variable anywhere in the component ✨
const store = useCounterStore()
</script>

提示

如果您还没有使用 setup 组件,您仍然可以使用映射助手与 Pinia 一起使用

您可以定义任意数量的存储,并且您应该在不同的文件中定义每个存储,以充分利用 Pinia(例如,自动允许您的捆绑器代码拆分并提供 TypeScript 推断)。

存储实例化后,您可以直接在存储上访问 stategettersactions 中定义的任何属性。我们将在接下来的页面中详细介绍这些内容,但自动完成功能将帮助您。

请注意,store 是一个用 reactive 包装的对象,这意味着不需要在获取器之后写 .value,但是,就像 setup 中的 props 一样,我们不能解构它

vue
<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()。它将为每个响应式属性创建引用。当您只使用存储中的状态而不调用任何操作时,这很有用。请注意,您可以直接从存储中解构操作,因为它们也绑定到存储本身

vue
<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>