text: '组合式 Stores',
link: '/zh/cookbook/composing-stores.html',
},
+ {
+ text: 'VSCode 代码片段',
+ link: '/zh/cookbook/vscode-snippets.html',
+ },
{
text: '从 v0/v1 迁移至 v2',
link: '/zh/cookbook/migration-v1-v2.html',
{
"zh": {
- "hash": "c67a5c9",
- "date": "2023-12-07"
+ "hash": "02a476d",
+ "date": "2024-05-20"
}
}
import { useMediaControls } from '@vueuse/core'
export const useVideoPlayer = defineStore('video', () => {
- // 我们不会直接暴露这个元素
+ // 我们不会直接暴露 (返回) 这个元素
const videoElement = ref<HTMLVideoElement>()
const src = ref('/data/video.mp4')
const { playing, volume, currentTime, togglePictureInPicture } =
})
```
+:::warning
+和常规的状态不同,`ref<HTMLVideoElement>()` 包含了一个不可序列化的 DOM 元素引用。这就是为什么我们不直接返回它的原因。由于它是客户端专用的状态,我们知道它不会被设置在服务器上,并且在客户端上**始终**以 `undefined` 作为开始。
+:::
+
## 服务端渲染 %{#ssr}%
当处理[服务端渲染](../ssr/index.md)时,你有一些需要额外注意的内容,以便在 store 中使用组合式函数。
Pinia 支持热更新,所以你可以编辑你的 store,并直接在你的应用中与它们互动,而不需要重新加载页面,允许你保持当前的 state、并添加甚至删除 state、action 和 getter。
-目前,只有 [Vite](https://vitejs.dev/) 被官方支持,不过任何实现 `import.meta.hot` 规范的构建工具都应该能正常工作。(例外的是,[webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) 似乎使用的是 `import.meta.webpackHot` 而不是 `import.meta.hot` )
+目前,只有 [Vite](https://cn.vitejs.dev/guide/api-hmr#hmr-api) 被官方支持,不过任何实现 `import.meta.hot` 规范的构建工具都应该能正常工作。(例外的是,[webpack](https://webpack.js.org/api/module-variables/#importmetawebpackhot) 似乎使用的是 `import.meta.webpackHot` 而不是 `import.meta.hot` )
你只需要在任何 store 声明旁边添加这段代码。比方说,你有三个 store:`auth.js`、 `cart.js` 和 `chat.js`, 你必须在每个 **store 声明**后都添加(和调整)这段代码。
```js
expect(store.someAction).toHaveBeenCalledTimes(1)
```
+<!-- TODO: translation -->
+
+### Mocking the returned value of an action
+
+Actions are automatically spied but type-wise, they are still the regular actions. In order to get the correct type, we must implement a custom type-wrapper that is applies the `Mock` type to each action. **This type depends on the testing framework you are using**. Here is an example with Vitest:
+
+```ts
+import type { Mock } from 'vitest'
+import type { Store, StoreDefinition } from 'pinia'
+
+function mockedStore<TStoreDef extends () => unknown>(
+ useStore: TStoreDef
+): TStoreDef extends StoreDefinition<
+ infer Id,
+ infer State,
+ infer Getters,
+ infer Actions
+>
+ ? Store<
+ Id,
+ State,
+ Getters,
+ {
+ [K in keyof Actions]: Actions[K] extends (
+ ...args: infer Args
+ ) => infer ReturnT
+ ? // 👇 depends on your testing framework
+ Mock<Args, ReturnT>
+ : Actions[K]
+ }
+ >
+ : ReturnType<TStoreDef> {
+ return useStore() as any
+}
+```
+
+This can be used in tests to get a correctly typed store:
+
+```ts
+import { mockedStore } from './mockedStore'
+import { useSomeStore } from '@/stores/myStore'
+
+const store = mockedStore(useSomeStore)
+// typed!
+store.someAction.mockResolvedValue('some value')
+```
+
### 指定 createSpy 函数 %{#specifying-the-createspy-function}%
当使用 Jest,或 vitest 且设置 `globals: true` 时,`createTestingPinia` 会自动使用现有测试框架 (`jest.fn` 或 `vitest.fn`) 的 spy 函数存根 (stub) action。如果你使用的是不同的框架,你需要提供一个 [createSpy](/zh/api/interfaces/pinia_testing.TestingOptions.html#createspy) 选项:
--- /dev/null
+# VS Code 代码片段
+
+有一些代码片段可以让你在 VS Code 中更轻松地使用 Pinia。
+
+通过 <kbd>⇧</kbd> <kbd>⌘</kbd> <kbd>P</kbd> / <kbd>⇧</kbd> <kbd>⌃</kbd> <kbd>P</kbd> 然后输入 `Snippets: Configure User Snippets` 就可以管理用户代码片段。
+
+```json
+{
+ "Pinia Options Store Boilerplate": {
+ "scope": "javascript,typescript",
+ "prefix": "pinia-options",
+ "body": [
+ "import { defineStore, acceptHMRUpdate } from 'pinia'",
+ "",
+ "export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', {",
+ " state: () => ({",
+ " $0",
+ " }),",
+ " getters: {},",
+ " actions: {},",
+ "})",
+ "",
+ "if (import.meta.hot) {",
+ " import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
+ "}",
+ ""
+ ],
+ "description": "Bootstrap the code needed for a Vue.js Pinia Options Store file"
+ },
+ "Pinia Setup Store Boilerplate": {
+ "scope": "javascript,typescript",
+ "prefix": "pinia-setup",
+ "body": [
+ "import { defineStore, acceptHMRUpdate } from 'pinia'",
+ "",
+ "export const use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store = defineStore('$TM_FILENAME_BASE', () => {",
+ " $0",
+ " return {}",
+ "})",
+ "",
+ "if (import.meta.hot) {",
+ " import.meta.hot.accept(acceptHMRUpdate(use${TM_FILENAME_BASE/^(.*)$/${1:/pascalcase}/}Store, import.meta.hot))",
+ "}",
+ ""
+ ],
+ "description": "Bootstrap the code needed for a Vue.js Pinia Setup Store file"
+ }
+}
+```
title="Learn all about actions in Pinia"
/>
-Action 相当于组件中的 [method](https://v3.vuejs.org/guide/data-methods.html#methods)。它们可以通过 `defineStore()` 中的 `actions` 属性来定义,**并且它们也是定义业务逻辑的完美选择。**
+Action 相当于组件中的 [method](https://cn.vuejs.org/api/options-state.html#methods)。它们可以通过 `defineStore()` 中的 `actions` 属性来定义,**并且它们也是定义业务逻辑的完美选择。**
```js
export const useCounterStore = defineStore('main', {
})
```
-类似 [getter](./getters.md),action 也可通过 `this` 访问**整个 store 实例**,并支持**完整的类型标注(以及自动补全✨)**。**不同的是,`action` 可以是异步的**,你可以在它们里面 `await` 调用任何 API,以及其他 action!下面是一个使用 [Mande](https://github.com/posva/mande) 的例子。请注意,你使用什么库并不重要,只要你得到的是一个`Promise`,你甚至可以 (在浏览器中) 使用原生 `fetch` 函数:
+类似 [getter](./getters.md),action 也可通过 `this` 访问**整个 store 实例**,并支持**完整的类型标注(以及自动补全✨)**。**不同的是,`action` 可以是异步的**,你可以在它们里面 `await` 调用任何 API,以及其他 action!下面是一个使用 [Mande](https://github.com/posva/mande) 的例子。请注意,你使用什么库并不重要,只要你得到的是一个`Promise`。你甚至可以 (在浏览器中) 使用原生 `fetch` 函数:
```js
import { mande } from 'mande'
## 订阅 action %{#subscribing-to-actions}%
-你可以通过 `store.$onAction()` 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。`after` 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,`onError` 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用,类似于[Vue docs 中的这个提示](https://v3.vuejs.org/guide/tooling/deployment.html#tracking-runtime-errors)。
+你可以通过 `store.$onAction()` 来监听 action 和它们的结果。传递给它的回调函数会在 action 本身之前执行。`after` 表示在 promise 解决之后,允许你在 action 解决后执行一个回调函数。同样地,`onError` 允许你在 action 抛出错误或 reject 时执行一个回调函数。这些函数对于追踪运行时错误非常有用,类似于[Vue docs 中的这个提示](https://cn.vuejs.org/guide/best-practices/production-deployment#tracking-runtime-errors)。
这里有一个例子,在运行 action 之前以及 action resolve/reject 之后打印日志记录。
})
```
-大多数时候,getter 仅依赖 state,不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 `this` 访问到**整个 store 实例**,**但(在 TypeScript 中)必须定义返回类型**。这是为了避免 TypeScript 的已知缺陷,**不过这不影响用箭头函数定义的 getter,也不会影响不使用 `this` 的 getter**。
+大多数时候,getter 仅依赖 state。不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 `this` 访问到**整个 store 实例**,**但(在 TypeScript 中)必须定义返回类型**。这是为了避免 TypeScript 的已知缺陷,**不过这不影响用箭头函数定义的 getter,也不会影响不使用 `this` 的 getter**。
```ts
export const useCounterStore = defineStore('counter', {
## 访问其他 getter %{#accessing-other-getters}%
-ä¸\8e计ç®\97å±\9eæ\80§ä¸\80æ ·ï¼\8cä½ ä¹\9få\8f¯ä»¥ç»\84å\90\88å¤\9a个 getterã\80\82é\80\9aè¿\87 `this`ï¼\8cä½ å\8f¯ä»¥è®¿é\97®å\88°å\85¶ä»\96ä»»ä½\95 getterã\80\82å\8d³ä½¿ä½ 没æ\9c\89使ç\94¨ TypeScriptï¼\8cä½ ä¹\9få\8f¯ä»¥ç\94¨ [JSDoc](https://jsdoc.app/tags-returns.html) æ\9d¥è®©ä½ ç\9a\84 IDE æ\8f\90示类å\9e\8b。
+ä¸\8e计ç®\97å±\9eæ\80§ä¸\80æ ·ï¼\8cä½ ä¹\9få\8f¯ä»¥ç»\84å\90\88å¤\9a个 getterã\80\82é\80\9aè¿\87 `this`ï¼\8cä½ å\8f¯ä»¥è®¿é\97®å\88°å\85¶ä»\96ä»»ä½\95 getterã\80\82å\9c¨è¿\99ç§\8dæ\83\85å\86µä¸\8bï¼\8c**ä½ é\9c\80è¦\81为è¿\99个 getter æ\8c\87å®\9aä¸\80个è¿\94å\9b\9eå\80¼ç\9a\84ç±»å\9e\8b**。
-```js
+::: code-group
+
+```ts [counterStore.ts]
+export const useCounterStore = defineStore('counter', {
+ state: () => ({
+ count: 0,
+ }),
+ getters: {
+ doubleCount(state) {
+ return state.count * 2
+ },
+ doubleCountPlusOne(): number {
+ return this.doubleCount + 1
+ },
+ },
+})
+```
+
+```js [counterStore.js]
+// 你可以在 JavaScript 中使用 JSDoc (https://jsdoc.app/tags-returns.html)
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
})
```
+:::
+
## 向 getter 传递参数 %{#passing-arguments-to-getters}%
_Getter_ 只是幕后的**计算**属性,所以不可以向它们传递任何参数。不过,你可以从 _getter_ 返回一个函数,该函数可以接受任意参数:
</template>
```
-请注意,当你这样做时,**getter 将不再被缓存**,它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好:
+请注意,当你这样做时,**getter 将不再被缓存**。它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好:
```js
export const useUserListStore = defineStore('userList', {
</script>
```
-这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**,始终尽量不要在同一组件中混合两种 API 样式。
+这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**。始终尽量不要在同一组件中混合两种 API 样式。
### 不使用 `setup()` %{#without-setup}%
```ts
import { inject } from 'vue'
import { useRoute } from 'vue-router'
+import { defineStore } from 'pinia'
export const useSearchFilters = defineStore('search-filters', () => {
const route = useRoute()
## 你应该选用哪种语法? %{#what-syntax-should-i-pick}%
-和[在 Vue 中如何选择组合式 API 与选项式 API](https://cn.vuejs.org/guide/introduction.html#which-to-choose) 一样,选择你觉得最舒服的那一个就好。如果你还不确定,可以先试试 [Option Store](#option-stores)。
+和[在 Vue 中如何选择组合式 API 与选项式 API](https://cn.vuejs.org/guide/introduction.html#which-to-choose) 一样,选择你觉得最舒服的那一个就好。两种语法都有各自的优势和劣势。Option Store 更容易使用,而 Setup Store 更灵活和强大。如果你想深入了解两者之间的区别,请查看 Mastering Pinia 中的 [Option Stores vs Setup Stores 章节](https://masteringpinia.com/lessons/when-to-choose-one-syntax-over-the-other)。
## 使用 Store %{#using-the-store}%
```vue
<script setup>
import { useCounterStore } from '@/stores/counter'
+import { computed } from 'vue'
+
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
```js
import { useUserStore } from '@/stores/user'
+import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
})
```
-任何由插件返回的属性都会被 devtools 自动追踪,所以如果你想在 devtools 中调试 `hello` 属性,为了使 devtools 能追踪到 `hello`,请确保**在 dev 模式下**将其添加到 `store._customProperties` 中:
+任何由插件*返回的*属性都会被 devtools 自动追踪,所以如果你想在 devtools 中调试 `hello` 属性,为了使 devtools 能追踪到 `hello`,请确保**在 dev 模式下**将其添加到 `store._customProperties` 中:
```js
// 上文示例
pinia.use(({ store }) => {
// 为了正确地处理 SSR,我们需要确保我们没有重写任何一个
// 现有的值
- if (!Object.prototype.hasOwnProperty(store.$state, 'hasError')) {
+ if (!store.$state.hasOwnProperty('hasError')) {
// 在插件中定义 hasError,因此每个 store 都有各自的
// hasError 状态
const hasError = ref(false)
```js
import { set, toRef } from '@vue/composition-api'
pinia.use(({ store }) => {
- if (!Object.prototype.hasOwnProperty(store.$state, 'hello')) {
+ if (!store.$state.hasOwnProperty('secret')) {
const secretRef = ref('secret')
// 如果这些数据是要在 SSR 过程中使用的
// 你应该将其设置在 `$state' 属性上
:::
+#### 重置插件中添加的 state
+
+默认情况下,`$reset()` 不会重置插件添加的 state,但你可以重写它来重置你添加的 state:
+
+```js
+import { toRef, ref } from 'vue'
+
+pinia.use(({ store }) => {
+ // 和上面的代码一样,只是为了参考
+ if (!store.$state.hasOwnProperty('hasError')) {
+ const hasError = ref(false)
+ store.$state.hasError = hasError
+ }
+ store.hasError = toRef(store.$state, 'hasError')
+
+ // 确认将上下文 (`this`) 设置为 store
+ const originalReset = store.$reset.bind(store)
+
+ // 覆写其 $reset 函数
+ return {
+ $reset() {
+ originalReset()
+ store.hasError = false
+ },
+ }
+})
+```
+
## 添加新的外部属性 %{#adding-new-external-properties}%
当添加外部属性、第三方库的类实例或非响应式的简单值时,你应该先用 `markRaw()` 来包装一下它,再将它传给 pinia。下面是一个在每个 store 中添加路由器的例子:
当[在 Nuxt 中使用 pinia](../ssr/nuxt.md) 时,你必须先创建一个 [Nuxt 插件](https://nuxt.com/docs/guide/directory-structure/plugins)。这样你才能访问到 `pinia` 实例:
-```ts
+```ts{14-16}
// plugins/myPiniaPlugin.js
import { PiniaPluginContext } from 'pinia'
import { Plugin } from '@nuxt/types'
export default myPlugin
```
+::: info
+
注意上面的例子使用的是 TypeScript。如果你使用的是 `.js` 文件,你必须删除类型标注 `PiniaPluginContext` 和 `Plugin` 以及它们的导入语句。
+
+:::
+
+### Nuxt.js 2
+
+如果你使用的是 Nuxt.js 2,其类型会稍有不同:
+
+```ts{3,15-17}
+// plugins/myPiniaPlugin.ts
+import { PiniaPluginContext } from 'pinia'
+import { Plugin } from '@nuxt/types'
+
+function MyPiniaPlugin({ store }: PiniaPluginContext) {
+ store.$subscribe((mutation) => {
+ // 响应 store 变更
+ console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}.`)
+ })
+
+ // 请注意,如果你使用的是 TS,则必须添加类型。
+ return { creationTime: new Date() }
+}
+
+const myPlugin: Plugin = ({ $pinia }) => {
+ $pinia.use(MyPiniaPlugin)
+}
+
+export default myPlugin
+```
store.count++
```
+注意,新的属性**如果没有在 `state()` 中被定义**,则不能被添加。它必须包含初始状态。例如:如果 `secondCount` 没有在 `state()` 中定义,我们无法执行 `store.secondCount = 2`。
+
## 重置 state %{#resetting-the-state}%
使用[选项式 API](/zh/core-concepts/index.md#option-stores) 时,你可以通过调用 store 的 `$reset()` 方法将 state 重置为初始值。
+# 开始
+
## 安装 %{#installation}%
+<VueMasteryLogoLink for="pinia-cheat-sheet">
+</VueMasteryLogoLink>
+
用你喜欢的包管理器安装 `pinia`:
```bash
另一方面,你应该避免在 Store 中引入那些原本可以在组件中保存的本地数据,例如,一个元素在页面中的可见性。
并非所有的应用都需要访问全局状态,但如果你的应用确实需要一个全局状态,那 Pinia 将使你的开发过程更轻松。
+
+## 什么时候**不**应该使用 Store
+
+有的时候我们会过度使用 store。如果觉得应用程序的 store 过多,你可能需要重新考虑使用 store 的目的。例如其中一些逻辑应该只是组合式函数,或者应该只是组件的本地状态。这在 Mastering Pinia 的 [(不要) 滥用 store](https://masteringpinia.com/lessons/not-overusing-stores) 课程中有详细介绍。
- title: ⚙️ 开发工具支持
details: 不管是 Vue 2 还是 Vue 3,支持 Vue devtools 钩子的 Pinia 都能给你更好的开发体验。
- title: 🔌 可扩展性
- details: 可通过事务、同步本地存储等方式扩展 Pinia,以响应 store 的变更。
+ details: 可通过事务、同步本地存储等方式扩展 Pinia,以响应 store 的变更以及 action。
- title: 🏗 模块化设计
details: 可构建多个 Store 并允许你的打包工具自动拆分它们。
- title: 📦 极致轻量化
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。如果你熟悉组合式 API 的话,你可能会认为可以通过一行简单的 `export const state = reactive({})` 来共享一个全局状态。对于单页应用来说确实可以,但如果应用在服务器端渲染,这可能会使你的应用暴露出一些[安全漏洞](https://cn.vuejs.org/guide/scaling-up/ssr#cross-request-state-pollution)。 而如果使用 Pinia,即使在小型单页应用中,你也可以获得如下功能:
+- 测试工具集
+- 插件:可通过插件扩展 Pinia 功能
+- 为 JS 开发者提供适当的 TypeScript 支持以及**自动补全**功能。
+- 支持服务端渲染
- Devtools 支持
- 追踪 actions、mutations 的时间线
- 在组件中展示它们所用到的 Store
- 不必重载页面即可修改 Store
- 开发时可保持当前的 State
- 插件:可通过插件扩展 Pinia 功能
-- 为 JS 开发者提供适当的 TypeScript 支持以及**自动补全**功能。
-- 支持服务端渲染
+
+如果你仍然有疑问,请查看[**官方**的 Mastering Pinia 课程](https://masteringpinia.com)。在一开始,我们会介绍如何创建自己的 `defineStore()` 函数,然后我们会转向官方的 Pinia API。
+
+<VueMasteryLogoLink for="pinia-cheat-sheet">
+</VueMasteryLogoLink>
## 基础示例 %{#basic-example}%
})
```
-然后你就可以在一个组件中使用该 store 了:
+然后你就可以在一个组件中*使用*该 store 了:
```vue
<script setup>
import { useCounterStore } from '@/stores/counter'
+
const counter = useCounterStore()
+
counter.count++
// 自动补全! ✨
counter.$patch({ count: counter.count + 1 })
// 或使用 action 代替
counter.increment()
</script>
+
<template>
<!-- 直接从 store 中访问 state -->
<div>Current Count: {{ counter.count }}</div>
</template>
```
+[在演练场中试一试](https://play.pinia.vuejs.org/#eNqNVM1O3DAQfpVpVGkXQWIQLYfVgqCIQ3toq9JjLsEZWNPEtuzJstUqb9IH6HP1STq2k/2hFeKyG49nvvnmsz+vsytri2WH2Sybe+mUJfBInb0otWqtcQRr6Dxem04TulsyDqGHe2damBRCpnDx6CelLrU02hMMQTh/Xjg9SEmpJv4fHpZaCHhStICqIyNNaxskZTT8+fV7m/zWViQX03UCn409Eggcwgn0DM5IxnFXpR+g0lDJCKSYFFb1Fkxp6bBFTYHQXKSxeWBeEHL/ipBXAPM3eQ5XUqL3QAsET7wDtXIoqfmZREjxoEqep6JaLS+uO+cYH+L0M1gPvDeE+34uQl5ov2mZHWVJ8rytLEtqNB/KOmCWw4YvMwYLkRCzSqsqRMpMxO8CfZvfOfPk45GU2dGYesknLGpckjGNzyurUtmCyPqZELLWnF9jo5au0EhC21b8U3N5VrwvTkSj7gQ3EkrXuNpvwxV5je1r0MfUy+Pi5F1xFlGXpwNoG1ADaF/qnmUhzzfrXj08EyVcFtWg+2LDOe+LUzWNefoUY+Q63FCUC5Q//hN/9KvE+qtDlm+JO2NR5R6Q0vbN7Wdc8fdmszV113D2C5vf0JumCxxT2odO10x7Jy+y/RjPmO/ud3+zItR+HCoQjWrE/Cjz9Qujb+meFqc7Km7NyhJuzF3jvdK4b+x4m6KjcRXTkrGfvwPnu8XTyYA/OUpUoltmMD2A84uRnOOnxWnuOtj4OHAbB2P3cripoWq8gTt2WkTntR+29yC3jwGjsJFh8LvfSLHj8zEEbFjlt29PiKTu4bc/yPq/puS2IQ==)
+
为实现更多高级用法,你甚至可以使用一个函数 (与组件 `setup()` 类似) 来定义一个 Store:
```js
})
```
+[在演练场中试一试](https://play.pinia.vuejs.org/#eNqNVEFu2zAQ/MpWKGAHscQGaXMwnCBpkEN7aIumR10Uah0zlUiCXCkuDP2kD+i7+pIuSVt20iLoSeJydnZ2yOUmu7K26DvM5tnCS6csgUfq7EWpVWuNI9hA5/HadJrQ3ZJxCAMsnWlhUgiZwsWDn5S61NJoT7ANwvnzxOlRAqWc+D0+LrUQ8KhoBVVHRprWNkjKaPj989ce/NpWJFfTTSKf72okEjiGExiYnJmM46pK30OloZKRSLEorOo9mdLSYYuagqCFSG1zw7wg5PoVIa8AFq/yHK6kRO+BVgieeAdq5VBS8yOZkOLBlTxPSbXqL64755gfYvdz2Gx1j4KHYSECLpQfS2azLFmet5VlS43mQ9kEznK74cuMyUIkxKzSqgqRMhPxv0Df5nfOPPp4JGU220Ev+YRFjT0Z0/i8siqlrYisnwsha834GhvVu0IjCW1b8VfO5VnxrjgRjboTXEgoXeP6aRnOyGts/4d9B718U5y8Lc4ia3+6JW0DayAdSj2wLeT5Zi3V/TNTwmVRDbrPNpzzU3OqpjGPH2OMXIejRLlC+f0f8Qe/Tqq/OGT7ejxoiyp3j5S2b24/4Zr/x83W1F3D6Bc2v6I3TRc0Jtj7Ttcs+wAX1X6IZ8x395u/WRNqv2sqCI1uRHy0+fqF1vdyT4vTAxf3w8oWjsPtcDkONBPzHI9bNS6VxqczHy9aHHZcR1ia+edPxPlh8nSyLT2ZwfQIzi+S1oPXgvGsY/qG5xFg2end4I5zuusuoou+ajoMT0fsLXwcv1lOs+YImO1TY/NH2fAHelGuuQ==)
+
如果你还不熟悉 setup() 函数和组合式 API,别担心,Pinia 也提供了一组类似 Vuex 的 [映射 state 的辅助函数](https://vuex.vuejs.org/zh/guide/state.html#mapstate-辅助函数)。你可以用和之前一样的方式来定义 Store,然后通过 `mapStores()`、`mapState()` 或 `mapActions()` 访问:
```js {22,24,28}
})
```
+[在演练场中试一试](https://play.pinia.vuejs.org/#eNqdVcFy0zAQ/RWNL0lpIrUUesikmRTooRyAoXDCHBxrm6i1JY8kp5nJ+N9ZS7bsOIFhekmk1b7dt0/a9T66LQq6LSGaRXOTalHYRSxFXihtyZ5weBQSPircS5CWVORRq5yMEDDqueVJ8WCVBjPxy8SCW92mVihpAqwQUiR9YGkweCktaIcPjpSl3kyfzMD/pzl2RnPjGUvYOV9knpSZ++9XMN7HkpAUt6UFPiNuSwhjRNkN6HBCCq0K0FaACR6U0rBeiy0YkqQpGEOsInYjDG04e3aJ5N5ak3MmD8YoQa7xoP7JQYFnk0E6DQk/mbNLxlW5ygaZ8DaOE/0aOeRoQkYeM/rt81XuNwe7Udz0BTpZspCphrwW9qyftLn4U2kDop+wQvSchfeHGwt5kSFz3BEy52K7cIGQ0B4vqQvZCFBVc1Y7Be9Prijn7us7dFmV1ipJlmkm0uebOAqs4mhx367nzLshZM4CoWgS+fc4xULx1SmJveNkwjDuwMRREC6O3KOvLXHE3JqCyacrrV78q42j5p7jaIl9xThsrVKZmSaF8LCNtYWZMZZyif4cMrHVVIJlssjZEWZ5Td/TS5aJFcNETEgOu8M0iJhyyP8neuu6vKCX7+i1i7q9aoLmdVR3hXiDKIs1qZKPYj0Qpe4pkYH+WrhHcSBOkmXq5bOzWV1CoJhuIH0+YX8yO8/6G7YP6C30yrKJXgNeYH189/AFdrgOh7niJTbGvw6/g1FZWXP0bh9KyZF2z8+xvXd3LOT6h7nbWZCmLaom2nWQk7meO38rvaN7Ra96KnaTDyUcTOLDwdeO0zD0UH5jj4bqTR889n0PGjvfUTH1fJiR8Rm5WZBx01wzckEq357IEb27SeC7CQEO6FBu1TTiG/K2N0YSPwcCuDcuWhPpzbHzc2/z4HYwoCbNgH+9IN1XY6BGHbmVop3xLmn1B2TmaJo=)
+
你将会在核心概念部分了解到更多关于每个**映射辅助函数**的信息。
+## 官方课程
+
+Pinia 的官方课程是 [Mastering Pinia](https://masteringpinia.com)。由 Pinia 的作者编写,它涵盖了从基础知识到诸如插件、测试和服务器端渲染之类的高级主题。这是学习 Pinia 和掌握它的最佳方式。
+
## 为什么取名 _Pinia_?%{#why-pinia}%
Pinia (发音为 `/piːnjʌ/`,类似英文中的 “peenya”) 是最接近有效包名 piña (西班牙语中的 _pineapple_,即“菠萝”) 的词。 菠萝花实际上是一组各自独立的花朵,它们结合在一起,由此形成一个多重的水果。 与 Store 类似,每一个都是独立诞生的,但最终它们都是相互联系的。 它(菠萝)也是一种原产于南美洲的美味热带水果。
})
```
+[在演练场中试一试](https://play.pinia.vuejs.org/#eNqtVs1y2zYQfpU1L5QdmUzGbQ4cyWO3k86kh7STuKcwB4pcWohJgIMfWRqVb9IH6HP1SboA+Cu7nkzbiygQu99++Haxy2Nw2zTRzmCQBCuVS9ZoUKhNc51yVjdCajiCxBJaKKWoISTTcLKltJB4Jz5iqQaThnGWTY2MIpNCjBZRrO06+qrILOW54EqDe/XJ4sF6cFmc99tHKFmlUS67JxY95nrKYjHCkGvvzPHRWt/hXpM5nWcRhm67NDzXTHDICoe3OIdjygFYCYuziVe0yyqD3SYQgjaS3AFaiwIT8lGP9NTbGj55S3xCUoFwVrFPAElPC411U2UaaQWwqrINVtcrxhujYXdZiwKrdRp4KdIA9KFBWsusYIKWDpnWWVWlwTXcVtUq9hD/Ba2kxKotFhbyp+7//4Fr+BT5t2E1w95K/zR+baMxilEKSQhWfmB8XhoUIXnAQ7cdMYvuXcn5lKM3Uf2xRrL5FvOHjdhPnI9Hl+9I23JqKXMOMa6YZxh3FDs5/PYHfATLKumsT+NP6mKMbQPQ6oZO0UhUKkJOx7N59TXWcZrptDFaUz0nBVPZpsKCrKeFbOHyiuUPM5TbgsT2noSyiofiC5aBv8aXddbQfRWcGoW7BGm3QTIn/bVIA3f37Zs0iN3/CFV9uZHiUaEk/zRY9qY31EriAndaiEpdZg3zblutG5XEcV5wsidx2E5GHHXMmzp+4nPzNvo+ekPSb2IKFDNe4H4ehjwuC6y/Bb03vXkdvfkueutQd1cdaG1RuxvfkixaUWsp2f2JKLmoG1ah/KWxbWUuDt1G8fize6elwYGiK7Fn3n9VVHWW9a+UfJQ7nBxLZ/IeKZt2+92nDy6zwyYVlanI+oXNj6hEZSxHb/aD4QXRntg5tu9djhm/v1Pv9hq56g9liTo1nL2T+ccXjj7SvYqupip2c4AEHMZFgdQA0E+C05mSctw7M9/Xh8mynnotQgcbLn18pamSE6DWvr6GRUcpvriAG3vN3G0mhRKyk3TQJbAiAW7qjZ01Y0dIYENFhxmH9vOXFi5ij+MiJfD5S6fbBDckBUP4HcK+n7nF2OzCEcX3rQScS48UuzYAj6yqYIOQGS3qTLOcbA7U7EqU1OmIQEfWe5E++j2Rfe1Q2nP3IOkJnmh2h+8Z+BHr9BlGmwtsY9lKrtCm8gz++uPPftePPi9q5NPn2S/c6HUinzRTN/j6UgEYFXg+/rdEOHs5BGWhQ6NseDz17xLdw8wS9U/M7VeD3rKeL6zXNNyHdE8Mncg2kSD0lgy7BFGu9fZE/Kn2gzZdkImKvUkLWCl8nsmk9GZcpqAnyRlgT5LjbF1upsL738x9UY3VZuuJHyCrheEaRAnUC0xNo0wte7gMGrrmjIgLCVxo79h/SdmszevzIAzJx6FgEnNN16E2NhVEC33d9LYjz6gxarvwJeBT7/b8fXn1al4BZWZFbGdVZX/b86D9GztAvyY=)
+
## 对比 Vuex %{#comparison-with-vuex}%
Pinia 起源于一次探索 Vuex 下一个迭代的实验,因此结合了 Vuex 5 核心团队讨论中的许多想法。最后,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分功能,所以决定将其作为新的推荐方案来代替 Vuex。