From 41eecd3e8629bf25f0c9893e67d6fe1e78bbeeac Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Tue, 11 Jun 2024 15:11:58 +0800 Subject: [PATCH] docs(zh): sync to 02a476d (#2674) * docs(zh): sync to 02a476d * docs(zh): translate the new docs * docs(zh): update the checkpoint * docs(zh): make up the vscode snippets page --- packages/docs/.vitepress/config/zh.ts | 4 ++ .../docs/.vitepress/translation-status.json | 4 +- packages/docs/zh/cookbook/composables.md | 6 +- .../zh/cookbook/hot-module-replacement.md | 2 +- packages/docs/zh/cookbook/testing.md | 47 +++++++++++++ packages/docs/zh/cookbook/vscode-snippets.md | 49 ++++++++++++++ packages/docs/zh/core-concepts/actions.md | 6 +- packages/docs/zh/core-concepts/getters.md | 31 +++++++-- packages/docs/zh/core-concepts/index.md | 5 +- .../core-concepts/outside-component-usage.md | 1 + packages/docs/zh/core-concepts/plugins.md | 66 +++++++++++++++++-- packages/docs/zh/core-concepts/state.md | 2 + packages/docs/zh/getting-started.md | 9 +++ packages/docs/zh/index.md | 2 +- packages/docs/zh/introduction.md | 28 +++++++- 15 files changed, 241 insertions(+), 21 deletions(-) create mode 100644 packages/docs/zh/cookbook/vscode-snippets.md diff --git a/packages/docs/.vitepress/config/zh.ts b/packages/docs/.vitepress/config/zh.ts index 5e1834e4..156e5ba6 100644 --- a/packages/docs/.vitepress/config/zh.ts +++ b/packages/docs/.vitepress/config/zh.ts @@ -142,6 +142,10 @@ export const zhConfig: LocaleSpecificConfig = { 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', diff --git a/packages/docs/.vitepress/translation-status.json b/packages/docs/.vitepress/translation-status.json index 57069675..7881b36b 100644 --- a/packages/docs/.vitepress/translation-status.json +++ b/packages/docs/.vitepress/translation-status.json @@ -1,6 +1,6 @@ { "zh": { - "hash": "c67a5c9", - "date": "2023-12-07" + "hash": "02a476d", + "date": "2024-05-20" } } diff --git a/packages/docs/zh/cookbook/composables.md b/packages/docs/zh/cookbook/composables.md index bc09d418..b8b6cd88 100644 --- a/packages/docs/zh/cookbook/composables.md +++ b/packages/docs/zh/cookbook/composables.md @@ -44,7 +44,7 @@ import { defineStore, skipHydrate } from 'pinia' import { useMediaControls } from '@vueuse/core' export const useVideoPlayer = defineStore('video', () => { - // 我们不会直接暴露这个元素 + // 我们不会直接暴露 (返回) 这个元素 const videoElement = ref() const src = ref('/data/video.mp4') const { playing, volume, currentTime, togglePictureInPicture } = @@ -67,6 +67,10 @@ export const useVideoPlayer = defineStore('video', () => { }) ``` +:::warning +和常规的状态不同,`ref()` 包含了一个不可序列化的 DOM 元素引用。这就是为什么我们不直接返回它的原因。由于它是客户端专用的状态,我们知道它不会被设置在服务器上,并且在客户端上**始终**以 `undefined` 作为开始。 +::: + ## 服务端渲染 %{#ssr}% 当处理[服务端渲染](../ssr/index.md)时,你有一些需要额外注意的内容,以便在 store 中使用组合式函数。 diff --git a/packages/docs/zh/cookbook/hot-module-replacement.md b/packages/docs/zh/cookbook/hot-module-replacement.md index 93a10b2c..68a9a671 100644 --- a/packages/docs/zh/cookbook/hot-module-replacement.md +++ b/packages/docs/zh/cookbook/hot-module-replacement.md @@ -2,7 +2,7 @@ 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 diff --git a/packages/docs/zh/cookbook/testing.md b/packages/docs/zh/cookbook/testing.md index ee8c108a..66dfe218 100644 --- a/packages/docs/zh/cookbook/testing.md +++ b/packages/docs/zh/cookbook/testing.md @@ -171,6 +171,53 @@ store.someAction() expect(store.someAction).toHaveBeenCalledTimes(1) ``` + + +### 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 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 + : Actions[K] + } + > + : ReturnType { + 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) 选项: diff --git a/packages/docs/zh/cookbook/vscode-snippets.md b/packages/docs/zh/cookbook/vscode-snippets.md new file mode 100644 index 00000000..7ef69955 --- /dev/null +++ b/packages/docs/zh/cookbook/vscode-snippets.md @@ -0,0 +1,49 @@ +# VS Code 代码片段 + +有一些代码片段可以让你在 VS Code 中更轻松地使用 Pinia。 + +通过 ⇧ ⌘ P / ⇧ ⌃ P 然后输入 `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" + } +} +``` diff --git a/packages/docs/zh/core-concepts/actions.md b/packages/docs/zh/core-concepts/actions.md index e8f6fed7..59bfaa69 100644 --- a/packages/docs/zh/core-concepts/actions.md +++ b/packages/docs/zh/core-concepts/actions.md @@ -10,7 +10,7 @@ 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', { @@ -28,7 +28,7 @@ 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' @@ -167,7 +167,7 @@ export default { ## 订阅 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 之后打印日志记录。 diff --git a/packages/docs/zh/core-concepts/getters.md b/packages/docs/zh/core-concepts/getters.md index 0e1810ca..f71cec68 100644 --- a/packages/docs/zh/core-concepts/getters.md +++ b/packages/docs/zh/core-concepts/getters.md @@ -23,7 +23,7 @@ export const useCounterStore = defineStore('counter', { }) ``` -大多数时候,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', { @@ -60,9 +60,28 @@ const store = useCounterStore() ## 访问其他 getter %{#accessing-other-getters}% -与计算属性一样,你也可以组合多个 getter。通过 `this`,你可以访问到其他任何 getter。即使你没有使用 TypeScript,你也可以用 [JSDoc](https://jsdoc.app/tags-returns.html) 来让你的 IDE 提示类型。 +与计算属性一样,你也可以组合多个 getter。通过 `this`,你可以访问到其他任何 getter。在这种情况下,**你需要为这个 getter 指定一个返回值的类型**。 -```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, @@ -85,6 +104,8 @@ export const useCounterStore = defineStore('counter', { }) ``` +::: + ## 向 getter 传递参数 %{#passing-arguments-to-getters}% _Getter_ 只是幕后的**计算**属性,所以不可以向它们传递任何参数。不过,你可以从 _getter_ 返回一个函数,该函数可以接受任意参数: @@ -115,7 +136,7 @@ const { getUserById } = storeToRefs(userList) ``` -请注意,当你这样做时,**getter 将不再被缓存**,它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好: +请注意,当你这样做时,**getter 将不再被缓存**。它们只是一个被你调用的函数。不过,你可以在 getter 本身中缓存一些结果,虽然这种做法并不常见,但有证明表明它的性能会更好: ```js export const useUserListStore = defineStore('userList', { @@ -210,7 +231,7 @@ export default defineComponent({ ``` -这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**,始终尽量不要在同一组件中混合两种 API 样式。 +这在将组件从选项式 API 迁移到组合式 API 时很有用,但**应该只是一个迁移步骤**。始终尽量不要在同一组件中混合两种 API 样式。 ### 不使用 `setup()` %{#without-setup}% diff --git a/packages/docs/zh/core-concepts/index.md b/packages/docs/zh/core-concepts/index.md index 1845a500..fdf7a0a2 100644 --- a/packages/docs/zh/core-concepts/index.md +++ b/packages/docs/zh/core-concepts/index.md @@ -81,6 +81,7 @@ Setup store 也可以依赖于全局**提供**的属性,比如路由。任何[ ```ts import { inject } from 'vue' import { useRoute } from 'vue-router' +import { defineStore } from 'pinia' export const useSearchFilters = defineStore('search-filters', () => { const route = useRoute() @@ -101,7 +102,7 @@ export const useSearchFilters = defineStore('search-filters', () => { ## 你应该选用哪种语法? %{#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}% @@ -128,6 +129,8 @@ const store = useCounterStore() ```vue + ``` +[在演练场中试一试](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 @@ -81,6 +93,8 @@ export const useCounterStore = defineStore('counter', () => { }) ``` +[在演练场中试一试](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} @@ -116,8 +130,14 @@ export default defineComponent({ }) ``` +[在演练场中试一试](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 类似,每一个都是独立诞生的,但最终它们都是相互联系的。 它(菠萝)也是一种原产于南美洲的美味热带水果。 @@ -169,6 +189,8 @@ export const useTodos = defineStore('todos', { }) ``` +[在演练场中试一试](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。 -- 2.47.3