Skip to content
目录导航

定义商店

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

js
import { defineStore } from 'pinia'
// 你可以为`defineStore()`的返回值命名任何你想要的名字,
// 但最好使用商店的名称并用`use`和`Store`包围它
//(例如`useUserStore`、`useCartStore`、`useProductStore` )

// 第一个参数是应用程序中商店的唯一 id
export const useStore = defineStore('main', {
  // other options...
})
1
2
3
4
5
6
7
8
9

这个 name,也称为 id,是必需的,Pinia 使用它来将商店连接到 devtools。 将返回的函数命名为 use... 是跨可组合项的约定,以使其用法惯用。

defineStore() 的第二个参数接受两个不同的值:Setup 函数或 Options 对象。

Option Stores

与 Vue 的 Options API 类似,我们也可以传递带有 stateactionsgetters 属性的 Options 对象。

js
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'Eduardo' }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})
1
2
3
4
5
6
7
8
9
10
11

您可以将state视为商店的data,将getters视为商店的computed属性,将actions视为methods

Option stores应该感觉直观且易于上手。

Setup Stores

还有另一种可能的语法来定义商店。 类似于 Vue Composition API 的 setup function,我们可以传入一个定义响应式属性和方法的函数,并返回一个带有属性和 我们要公开的方法。

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 }
})
1
2
3
4
5
6
7
8
9
10

Setup Stores 中:

  • ref() 成为 state 的属性
  • computed() 变成 getters
  • function() 变成 actions

设置商店比 Options Stores 带来更多的灵活性,因为您可以在商店内创建观察者并自由使用任何 composable。 但是,请记住,使用 SSR 时,使用可组合项会变得更加复杂。

我应该选择什么语法?

Vue 的 Composition API 和 Options API 一样,选择您觉得最舒服的一个。 如果您不确定,请先尝试 Option Stores

使用商店

我们正在_定义_一个 store,因为在 setup() 内部调用 use...Store() 之前不会创建 store:

js
import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const store = useCounterStore()

    return {
      // 您可以返回整个商店实例以在模板中使用它
      store,
    }
  },
}
1
2
3
4
5
6
7
8
9
10
11
12

TIP

如果您还没有使用 setup 组件,您仍然可以将 Pinia 与 map helpers 一起使用

您可以根据需要定义任意数量的商店,并且您应该在不同的文件中定义每个商店以充分利用 Pinia(例如自动允许您的捆绑器进行代码拆分并提供 TypeScript 推理)。

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

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

js
export default defineComponent({
  setup() {
    const store = useCounterStore()
    // ❌ 这不会工作,因为它破坏了反应性
    // 这和从 `props` 解构是一样的
    const { name, doubleCount } = store

    name // "Eduardo"
    doubleCount // 0

    setTimeout(() => {
      store.increment()
    }, 1000)

    return {
      name, // 将永远是 "Eduardo"
      doubleCount, // 将永远是 0
      doubleNumber: store.doubleCount, //也将永远是0
      doubleValue: computed(() => store.doubleCount), // ✅ 这个是响应式的
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

为了从商店中提取属性同时保持其反应性,您需要使用storeToRefs() 。 它将为每个反应属性创建引用。 当您仅使用商店中的状态而不调用任何操作时,这很有用。 请注意,您可以直接从商店中解构操作,因为它们也绑定到商店本身:

js
import { storeToRefs } from 'pinia'

export default defineComponent({
  setup() {
    const store = useCounterStore()
     // `name` 和 `doubleCount` 是响应式引用
     // 这也将为插件添加的属性创建引用
     // 但跳过任何动作或非反应性(非参考/反应性)属性
    const { name, doubleCount } = storeToRefs(store)
    // 可以提取增量动作
    const { increment } = store

    return {
      name,
      doubleCount,
      increment,
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19