Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
Pinia
https://pinia.vuejs.org/zh/
Admin
Pinia是一个符合直觉的 Vue.js 状态管理库,提供类型安全、可扩展性和模块化设计,旨在提供极致轻量和易于使用的状态管理体验。
Tokens:
38,219
Snippets:
365
Trust Score:
10
Update:
5 months ago
Context
Chat
Benchmark
83.76
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### Use Various Composables in Setup Store Source: https://pinia.vuejs.org/zh/cookbook/composables.html In a Setup Store, you can use almost any composable because each property is identified as state, action, or getter. This example uses `useMediaControls` from `@vueuse/core`. ```typescript import { defineStore, skipHydrate } from 'pinia' import { useMediaControls } from '@vueuse/core' export const useVideoPlayer = defineStore('video', () => { // We won't expose this element directly (return) const videoElement = ref<HTMLVideoElement>() const src = ref('/data/video.mp4') const { playing, volume, currentTime, togglePictureInPicture } = useMediaControls(video, { src }) function loadVideo(element: HTMLVideoElement, src: string) { videoElement.value = element src.value = src } return { src, playing, volume, currentTime, loadVideo, togglePictureInPicture, } }) ``` -------------------------------- ### Test Pinia Store with Plugins Source: https://pinia.vuejs.org/zh/cookbook/testing.html When testing stores with plugins, ensure the plugin is used with the `pinia` instance after it's installed on an app. This example shows creating a dummy app to handle plugin installation. ```javascript import { setActivePinia, createPinia } from 'pinia' import { createApp } from 'vue' import { somePlugin } from '../src/stores/plugin' // 和前面一样的代码... // 测试前你不需要创建应用 const app = createApp({}) beforeEach(() => { const pinia = createPinia().use(somePlugin) app.use(pinia) setActivePinia(pinia) }) ``` -------------------------------- ### Setup Store Syntax Source: https://pinia.vuejs.org/zh/core-concepts Define a Pinia store using the Setup Store syntax, similar to Vue's Composition API `setup` function. It uses `ref`, `computed`, and functions for state, getters, and actions. Ensure all state properties are returned. ```js import { ref, computed } from 'vue' import { defineStore } from 'pinia' 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 } }) ``` -------------------------------- ### Custom Options with Setup Store Syntax Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html When using the setup store syntax, custom options like 'debounce' are passed as the third argument to defineStore. ```javascript defineStore( 'search', () => { // ... }, { // 这将在后面被一个插件读取 debounce: { // 让 action searchContacts 防抖 300ms searchContacts: 300, }, } ) ``` -------------------------------- ### Define Store with Setup API Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Create a `useStore` function for retrieving a store instance using the setup API. This approach uses a function that returns state, getters, and actions. ```typescript defineStore('counter', () => { const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } return { count, double, increment } }) ``` -------------------------------- ### Using Vuex store in a component Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Example of accessing state and getters using Vuex's useStore and computed properties within a component's setup function. ```typescript import { defineComponent, computed } from 'vue' import { useStore } from 'vuex' export default defineComponent({ setup() { const store = useStore() const firstName = computed(() => store.state.auth.user.firstName) const fullName = computed(() => store.getters['auth/user/fullName']) return { firstName, fullName, } }, }) ``` -------------------------------- ### Install Pinia v1 (0.x) Source: https://pinia.vuejs.org/zh/cookbook/migration-v1-v2.html Install the latest 0.x version of Pinia to prepare for migration. This ensures you are on the latest version before upgrading. ```shell npm i 'pinia@^0.x.x' # 或者使用 yarn yarn add 'pinia@^0.x.x' ``` -------------------------------- ### Using Pinia store in a component Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Example of accessing state and getters using a Pinia store within a component's setup function. The entire store can also be returned for direct access. ```typescript import { defineComponent, computed } from 'vue' import { useAuthUserStore } from '@/stores/auth-user' export default defineComponent({ setup() { const authUserStore = useAuthUserStore() const firstName = computed(() => authUserStore.firstName) const fullName = computed(() => authUserStore.fullName) return { // 你也可以在你的组件中通过返回 store 来访问整个 store authUserStore, firstName, fullName, } }, }) ``` -------------------------------- ### Setup Store with Injected Dependencies Source: https://pinia.vuejs.org/zh/core-concepts Setup stores can inject global properties like routes or other provided values using `inject()`. Avoid returning injected properties as part of the store's state. ```ts import { inject } from 'vue' import { useRoute } from 'vue-router' import { defineStore } from 'pinia' export const useSearchFilters = defineStore('search-filters', () => { const route = useRoute() // 这里假定 `app.provide('appProvided', 'value')` 已经调用过 const appProvided = inject('appProvided') // ... return { // ... } }) ``` -------------------------------- ### Vuex Module Example Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html An example of a Vuex module with namespaced state, getters, actions, and mutations. ```typescript // 'auth/user' 命名空间中的 Vuex 模块 import { Module } from 'vuex' import { api } from '@/api' import { RootState } from '@/types' // 如果需要使用 Vuex 的类型便需要引入 interface State { firstName: string lastName: string userId: number | null } const storeModule: Module<State, RootState> = { namespaced: true, state: { firstName: '', lastName: '', userId: null, }, getters: { firstName: (state) => state.firstName, fullName: (state) => `${state.firstName} ${state.lastName}`, loggedIn: (state) => state.userId !== null, // 与其他模块的一些状态相结合 fullUserDetails: (state, getters, rootState, rootGetters) => { return { ...state, fullName: getters.fullName, // 读取另一个名为 `auth` 模块的 state ...rootState.auth.preferences, // 读取嵌套于 `auth` 模块的 `email` 模块的 getter ...rootGetters['auth/email'].details, } }, }, actions: { async loadUser({ state, commit }, id: number) { if (state.userId !== null) throw new Error('Already logged in') const res = await api.user.load(id) commit('updateUser', res) }, }, mutations: { updateUser(state, payload) { state.firstName = payload.firstName state.lastName = payload.lastName state.userId = payload.userId }, clearUser(state) { state.firstName = '' state.lastName = '' state.userId = null }, }, } export default storeModule ``` -------------------------------- ### Using Getters with Options API and `setup()` Hook Source: https://pinia.vuejs.org/zh/core-concepts/getters.html The `setup()` hook in the Options API allows easier integration with Pinia getters without needing mapping helpers. Computed properties can then access these getters. ```vue <script> import { useCounterStore } from '../stores/counter' export default defineComponent({ setup() { const counterStore = useCounterStore() return { counterStore } }, computed: { quadrupleCounter() { return this.counterStore.doubleCount * 2 }, }, }) </script> ``` -------------------------------- ### Use Actions with Composition API `setup()` Source: https://pinia.vuejs.org/zh/core-concepts/actions.html Demonstrates how to access and use Pinia actions within the `setup()` hook of a Vue component using the Composition API. The store instance is returned and can be used in the template. ```vue <script> import { useCounterStore } from '../stores/counter' export default defineComponent({ setup() { const counterStore = useCounterStore() return { counterStore } }, methods: { incrementAndPrint() { this.counterStore.increment() console.log('New Count:', this.counterStore.count) }, }, }) </script> ``` -------------------------------- ### Using Nested Stores in Setup Stores Source: https://pinia.vuejs.org/zh/cookbook/composing-stores.html Shows how to import and use another store within the setup function of a Pinia store. This allows access to the nested store's state, getters, and actions. ```typescript import { useUserStore } from './user' export const useCartStore = defineStore('cart', () => { const user = useUserStore() const list = ref([]) const summary = computed(() => { return `Hi ${user.name}, you have ${list.value.length} items in your cart. It costs ${price.value}.` }) function purchase() { return apiPurchase(user.id, this.list) } return { summary, purchase } }) ``` -------------------------------- ### Install Pinia for Nuxt 2 Source: https://pinia.vuejs.org/zh/ssr/nuxt.html For Nuxt 2, install Pinia, the Nuxt module, and the composition API module. Ensure you use a compatible version of '@pinia/nuxt' (e.g., v0.2.1). ```bash yarn add pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api # 使用 npm npm install pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api ``` -------------------------------- ### Using Pinia Store in Setup Function (SSR) Source: https://pinia.vuejs.org/zh/ssr This is the standard way to use Pinia stores within a `setup` function in an SSR context. Pinia automatically detects the correct store instance when called within `setup`. ```vue <script setup> // This works because pinia knows what is running in `setup`. const main = useMainStore() </script> ``` -------------------------------- ### Install Pinia with Yarn or npm Source: https://pinia.vuejs.org/zh/getting-started.html Use your preferred package manager to add Pinia to your project. If using Vue 2, you'll also need to install `@vue/composition-api`. ```bash yarn add pinia # 或者使用 npm npm install pinia ``` -------------------------------- ### Pinia Setup Store Boilerplate Snippet Source: https://pinia.vuejs.org/zh/cookbook/vscode-snippets.html Use this snippet to quickly generate the basic structure for a Pinia Setup Store. It includes imports, the store definition, and HMR support. ```json { "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" } } ``` -------------------------------- ### Install Pinia Nuxt package Source: https://pinia.vuejs.org/zh/cookbook/migration-v1-v2.html Install the dedicated Nuxt package for Pinia. This simplifies integration with Nuxt.js applications. ```bash npm i @pinia/nuxt # 或者使用 yarn yarn add @pinia/nuxt ``` -------------------------------- ### _ExtractActionsFromSetupStore Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Internal utility type to extract actions from a Setup Store. ```APIDOC Ƭ **_ExtractActionsFromSetupStore** <`SS`>: `SS` extends `undefined` | `void` ? {} : `_ExtractActionsFromSetupStore_Keys`<`SS`> extends keyof `SS` ? `Pick`<`SS`, `_ExtractActionsFromSetupStore_Keys`<`SS`>> : `never` **仅** 供内部使用 #### 类型参数 名称 --- `SS` ``` -------------------------------- ### defineStore Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Creates a `useStore` function to retrieve a store instance. Supports both options and setup stores. ```APIDOC ## defineStore (Options Store) ### Description Creates a `useStore` function to retrieve a store instance. ### Type Parameters - `Id`: extends `string` - `S`: extends `StateTree` = {} - `G`: extends `_GettersTree`<`S`> = {} - `A`: {} ### Parameters - `id` (`Id`): id of the store (must be unique) - `options` (`Omit`<`DefineStoreOptions`<`Id`, `S`, `G`, `A`>, ` ``` ```APIDOC `id`>): options to define the store ### Returns `StoreDefinition`<`Id`, `S`, `G`, `A`> ``` ```APIDOC ## defineStore (Options Store - No ID) ### Description Creates a `useStore` function to retrieve a store instance. ### Type Parameters - `Id`: extends `string` - `S`: extends `StateTree` = {} - `G`: extends `_GettersTree`<`S`> = {} - `A`: {} ### Parameters - `options` (`DefineStoreOptions`<`Id`, `S`, `G`, `A`>): options to define the store ### Returns `StoreDefinition`<`Id`, `S`, `G`, `A`> ``` ```APIDOC ## defineStore (Setup Store) ### Description Creates a `useStore` function to retrieve a store instance. ### Type Parameters - `Id`: extends `string` - `SS`: `SS` ### Parameters - `id` (`Id`): id of the store (must be unique) - `storeSetup` (() => `SS`): function that defines the store - `options?` (`DefineSetupStoreOptions`<`Id`, `_ExtractStateFromSetupStore`<`SS`>, `_ExtractGettersFromSetupStore`<`SS`>, `_ExtractActionsFromSetupStore`<`SS`>>): extra options ### Returns `StoreDefinition`<`Id`, `_ExtractStateFromSetupStore`<`SS`>, `_ExtractGettersFromSetupStore`<`SS`>, `_ExtractActionsFromSetupStore`<`SS`>> ``` -------------------------------- ### Pinia Store Conversion Example Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Converts the Vuex module to a Pinia option store. Demonstrates state, getters, actions, and mutation conversion. ```typescript // Pinia Store import { defineStore } from 'pinia' import { useAuthPreferencesStore } from './auth-preferences' import { useAuthEmailStore } from './auth-email' import vuexStore from '@/store' // 逐步转换,见 fullUserDetails interface State { firstName: string lastName: string userId: number | null } export const useAuthUserStore = defineStore('auth/user', { // 转换为函数 state: (): State => ({ firstName: '', lastName: '', userId: null, }), getters: { // 不在需要 firstName getter,移除 fullName: (state) => `${state.firstName} ${state.lastName}`, loggedIn: (state) => state.userId !== null, // 由于使用了 `this`,必须定义一个返回类型 fullUserDetails(state): FullUserDetails { // 导入其他 store const authPreferencesStore = useAuthPreferencesStore() const authEmailStore = useAuthEmailStore() return { ...state, // `this` 上的其他 getter fullName: this.fullName, ...authPreferencesStore.$state, ...authEmailStore.details, } // 如果其他模块仍在 Vuex 中,可替代为 // return { // ...state, // fullName: this.fullName, // ...vuexStore.state.auth.preferences, // ...vuexStore.getters['auth/email'].details // } }, }, actions: { //没有作为第一个参数的上下文,用 `this` 代替 async loadUser(id: number) { if (this.userId !== null) throw new Error('Already logged in') const res = await api.user.load(id) this.updateUser(res) }, // mutation 现在可以成为 action 了,不再用 `state` 作为第一个参数,而是用 `this`。 updateUser(payload) { this.firstName = payload.firstName this.lastName = payload.lastName this.userId = payload.userId }, // 使用 `$reset` 可以轻松重置 state clearUser() { this.$reset() }, }, }) ``` -------------------------------- ### Install Pinia with Nuxt Source: https://pinia.vuejs.org/zh/ssr/nuxt.html Install Pinia and its Nuxt module using Yarn or npm. If using npm, you might need to add an override for Vue to resolve dependency issues. ```bash yarn add pinia @pinia/nuxt # 或者使用 npm npm install pinia @pinia/nuxt ``` ```json "overrides": { "vue": "latest" } ``` -------------------------------- ### Define Getter with Options API Source: https://pinia.vuejs.org/zh/core-concepts/getters.html This example shows how to define a getter using the Options API style within `defineStore`. ```javascript // Example file path: // ./src/stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), getters: { doubleCount(state) { return state.count * 2 }, }, }) ``` -------------------------------- ### Map State and Getters with mapState Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Use `mapState` to map state properties and getters from a store to a component's computed properties. This allows access without using the `setup()` function. The example shows mapping directly from an array of state properties and getters. ```javascript export default { computed: { // 其他计算属性 ...mapState(useCounterStore, ['count', 'double']), }, created() { this.count // 2 this.double // 4 }, } ``` -------------------------------- ### Add Static Property to All Stores with a Plugin Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html This example shows how to add a static property `secret` to every store created after the plugin is installed. This is useful for global objects like routers or modal managers. ```javascript import { createPinia } from 'pinia' // Creates a secret property for every store. // Plugins can be saved in different files once installed. function SecretPiniaPlugin() { return { secret: 'the cake is a lie' } } const pinia = createPinia() // Pass the plugin to Pinia pinia.use(SecretPiniaPlugin) // In another file // const store = useStore() // store.secret // 'the cake is a lie' ``` -------------------------------- ### Map Multiple Stores with mapStores Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Use `mapStores` to map multiple store instances to a component's computed properties. This allows access to different stores without using the `setup()` function. The example shows mapping two stores, `useUserStore` and `useCartStore`. ```javascript export default { computed: { // 其他计算属性 ...mapStores(useUserStore, useCartStore), }, created() { this.userStore // id 为 "user" 的 store this.cartStore // id 为 "cart" 的 store }, } ``` -------------------------------- ### Create Pinia Instance for Vue 2 Source: https://pinia.vuejs.org/zh/getting-started.html For Vue 2 applications, install the `PiniaVuePlugin` and use `Vue.use()` to integrate Pinia. Ensure the `pinia` instance is passed during the Vue app initialization. ```javascript import { createPinia, PiniaVuePlugin } from 'pinia' Vue.use(PiniaVuePlugin) const pinia = createPinia() new Vue({ el: '#app', // 其他配置... // ... // 请注意,同一个`pinia'实例 // 可以在同一个页面的多个 Vue 应用中使用。 pinia, }) ``` -------------------------------- ### Use Store Outside Setup in Nuxt Source: https://pinia.vuejs.org/zh/ssr/nuxt.html When using a store outside of the setup() function in Nuxt, pass the pinia object to useStore() to make it available in contexts like asyncData() and fetch(). ```javascript import { useStore } from '~/stores/myStore' export default { asyncData({ $pinia }) { const store = useStore($pinia) }, } ``` ```vue <script setup> const store = useStore() const { data } = await useAsyncData('user', () => store.fetchUser()) </script> ``` -------------------------------- ### Handle SSR in Setup Store with skipHydrate() Source: https://pinia.vuejs.org/zh/cookbook/composables.html In Setup Stores for SSR, use the `skipHydrate()` helper for state properties that should not receive initial state. This marks properties that cannot be activated, applicable only to writable reactive properties. ```typescript import { defineStore, skipHydrate } from 'pinia' import { useEyeDropper, useLocalStorage } from '@vueuse/core' const useColorStore = defineStore('colors', () => { const { isSupported, open, sRGBHex } = useEyeDropper() const lastColor = useLocalStorage('lastColor', sRGBHex) // ... return { lastColor: skipHydrate(lastColor), // Ref<string> open, // Function isSupported, // boolean (not reactive) } }) ``` -------------------------------- ### Using Pinia Store Outside Setup in SSR Source: https://pinia.vuejs.org/zh/ssr When using a store outside of a `setup` function, you must explicitly pass the `pinia` instance to `useStore()`. This ensures the correct store is used within the application's lifecycle, such as in `beforeEach` route guards. ```javascript const pinia = createPinia() const app = createApp(App) app.use(router) app.use(pinia) router.beforeEach((to) => { // ✅ This will work fine as it ensures the correct store is used for // the currently running app const main = useMainStore(pinia) if (to.meta.requiresAuth && !main.isLoggedIn) return '/login' }) ``` -------------------------------- ### Using a Store in a Vue Component Source: https://pinia.vuejs.org/zh/core-concepts Instantiate a store within a Vue component using `<script setup>` by calling the `use...Store()` function. The store instance can then be accessed directly. ```vue <script setup> import { useCounterStore } from '@/stores/counter' // 在组件内部的任何地方均可以访问变量 `store` ✨ const store = useCounterStore() </script> ``` -------------------------------- ### Nuxt 2 Pinia Plugin Integration Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html Example of a Nuxt 2 plugin for Pinia, demonstrating store subscription and returning custom properties, with TypeScript annotations. ```typescript // 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 ``` -------------------------------- ### Accessing Getters with `setup()` in Composition API Source: https://pinia.vuejs.org/zh/core-concepts/getters.html In the Composition API, getters are accessed directly as properties of the store instance returned by `useCounterStore()`. ```vue <script setup> const store = useCounterStore() store.count = 3 store.doubleCount // 6 </script> ``` -------------------------------- ### SSR State Serialization with vite-ssr and @nuxt/devalue Source: https://pinia.vuejs.org/zh/ssr This example demonstrates using `viteSSR` with the `transformState` option and `@nuxt/devalue` for SSR state serialization. It shows how to set `initialState.pinia` on the server and restore it on the client. ```javascript import devalue from '@nuxt/devalue' export default viteSSR( App, { routes, transformState(state) { return import.meta.env.SSR ? devalue(state) : state }, }, ({ initialState }) => { // ... if (import.meta.env.SSR) { // Serialize and set as window.__INITIAL_STATE__ initialState.pinia = pinia.state.value } else { // On the client, we restore the state pinia.state.value = initialState.pinia } } ) ``` -------------------------------- ### StoreActions Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Extracts the actions from a store type, compatible with both Setup and Option Stores. ```APIDOC Ƭ **StoreActions** <`SS`>: `SS` extends `Store`<`string`, `StateTree`, `_GettersTree`<`StateTree`>, `infer A`> ? `A` : `_ExtractActionsFromSetupStore`<`SS`> 提取一个 store 类型的 action。 对 Setup Store 或 Option Store 都有效。 #### 类型参数 名称 --- `SS` ``` -------------------------------- ### Initialize Pinia Before Accessing Stores in SPA Source: https://pinia.vuejs.org/zh/core-concepts/outside-component-usage.html In a Single Page Application without SSR, ensure Pinia is installed using `app.use(pinia)` before calling `useStore()`. Accessing stores before Pinia is active will lead to errors. ```javascript import { useUserStore } from '@/stores/user' import { createPinia } from 'pinia' import { createApp } from 'vue' import App from './App.vue' // ❌ 失败,因为它是在创建 pinia 之前被调用的 const userStore = useUserStore() const pinia = createPinia() const app = createApp(App) app.use(pinia) // ✅ 成功,因为 pinia 实例现在激活了 const userStore = useUserStore() ``` -------------------------------- ### Define a More Realistic Todos Store with TypeScript Source: https://pinia.vuejs.org/zh/introduction.html An example of a comprehensive Pinia store for managing todos, demonstrating state, getters, actions, and TypeScript type hints for better developer experience. ```javascript import { defineStore } from 'pinia' export const useTodos = defineStore('todos', { state: () => ({ /** @type {{ text: string, id: number, isFinished: boolean }[]} */ todos: [], /** @type {'all' | 'finished' | 'unfinished'} */ filter: 'all', // 类型将自动推断为 number nextId: 0, }), getters: { finishedTodos(state) { // 自动补全! ✨ return state.todos.filter((todo) => todo.isFinished) }, unfinishedTodos(state) { return state.todos.filter((todo) => !todo.isFinished) }, /** * @returns {{ text: string, id: number, isFinished: boolean }[]} */ filteredTodos(state) { if (this.filter === 'finished') { // 调用其他带有自动补全的 getters ✨ return this.finishedTodos } else if (this.filter === 'unfinished') { return this.unfinishedTodos } return this.todos }, }, actions: { // 接受任何数量的参数,返回一个 Promise 或不返回 addTodo(text) { // 你可以直接变更该状态 this.todos.push({ text, id: this.nextId++, isFinished: false }) }, }, }) ``` -------------------------------- ### Vue 2 Component Unit Testing with Pinia Source: https://pinia.vuejs.org/zh/cookbook/testing.html For Vue Test Utils v1, install Pinia on `localVue` using `localVue.use(PiniaVuePlugin)` before mounting the component. ```javascript import { PiniaVuePlugin } from 'pinia' import { createLocalVue, mount } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' const localVue = createLocalVue() localVue.use(PiniaVuePlugin) const wrapper = mount(Counter, { localVue, pinia: createTestingPinia(), }) const store = useSomeStore() // use the testing instance of pinia! ``` -------------------------------- ### SSR State Activation with JSON.stringify Source: https://pinia.vuejs.org/zh/ssr This example shows an alternative to `@nuxt/devalue` for state serialization and deserialization using `JSON.stringify` and `JSON.parse`. It assumes the state is serialized into a `<script>` tag and accessed globally via `window.__pinia` on the client. ```javascript const pinia = createPinia() const app = createApp(App) app.use(pinia) // Must be set by the user if (isClient) { pinia.state.value = JSON.parse(window.__pinia) } ``` -------------------------------- ### Reset Store State Source: https://pinia.vuejs.org/zh/core-concepts/state.html Use the $reset() method to reset the store's state to its initial values. For Setup Stores, you need to define your own $reset() method. ```js const store = useStore() store.$reset() ``` ```ts export const useCounterStore = defineStore('counter', () => { const count = ref(0) function $reset() { count.value = 0 } return { count, $reset } }) ``` -------------------------------- ### Define Actions with Options API Source: https://pinia.vuejs.org/zh/core-concepts/actions.html Example of defining a simple action within the `actions` property when using Pinia with the Options API. This action increments a counter. ```javascript // 示例文件路径: // ./src/stores/counter.js import { defineStore } from 'pinia' const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), actions: { increment() { this.count++ }, }, }) ``` -------------------------------- ### State Activation for SSR with @nuxt/devalue Source: https://pinia.vuejs.org/zh/ssr To activate initial state on the server, ensure `rootState` is included in the HTML. For security, especially if state can be user-modified, escape the state using a library like `@nuxt/devalue`. This example shows serialization and placement in the HTML. ```javascript import devalue from '@nuxt/devalue' import { createPinia } from 'pinia' // Retrieve the rootState from the server const pinia = createPinia() const app = createApp(App) app.use(router) app.use(pinia) // After rendering the page, rootState is established and can be read directly on `pinia.state.value`. // Serialize, escape (this is very important if the content of the state can be changed by the user, which is almost always the case) // and place it somewhere in the page // For example, as a global variable. devalue(pinia.state.value) ``` -------------------------------- ### Plugin to Handle Custom Store Options (Debounce Example) Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html A plugin that reads custom 'debounce' options from store definitions and wraps specified actions with a debounce function from a library like lodash. ```javascript // 使用任意防抖库 import debounce from 'lodash/debounce' pinia.use(({ options, store }) => { if (options.debounce) { // 我们正在用新的 action 来覆盖这些 action return Object.keys(options.debounce).reduce((debouncedActions, action) => { debouncedActions[action] = debounce( store[action], options.debounce[action] ) return debouncedActions }, {}) } }) ``` -------------------------------- ### Using Pinia store in router guards Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Example of accessing a Pinia store within a Vue Router navigation guard. It's crucial to instantiate the store inside the function. ```typescript import { useAuthUserStore } from '@/stores/auth-user' router.beforeEach((to, from, next) => { // 必须在函数内部使用 const authUserStore = useAuthUserStore() if (authUserStore.loggedIn) next() else next('/login') }) ``` -------------------------------- ### Add External Properties with `markRaw` Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html When adding external properties like class instances or non-reactive values, wrap them with `markRaw()` before passing them to Pinia. This example adds a router instance to each store. ```javascript import { markRaw } from 'vue' // Adjust the import path according to your router location import { router } from './router' pinia.use(({ store }) => { store.router = markRaw(router) }) ``` -------------------------------- ### Create Pinia Instance for Vue 3 Source: https://pinia.vuejs.org/zh/getting-started.html Create a Pinia instance and pass it to your Vue application using `app.use()`. This sets up the root store and enables devtools support. ```javascript import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const pinia = createPinia() const app = createApp(App) app.use(pinia) app.mount('#app') ``` -------------------------------- ### Use Stores in Vue Router Navigation Guards Source: https://pinia.vuejs.org/zh/core-concepts/outside-component-usage.html To use stores within Vue Router's navigation guards, ensure the store is accessed after Pinia has been installed. Accessing it before installation, especially due to import order, will cause failures. ```javascript import { createRouter } from 'vue-router' const router = createRouter({ // ... }) // ❌ 由于引入顺序的问题,这将失败 const store = useStore() router.beforeEach((to, from, next) => { // 我们想要在这里使用 store if (store.isLoggedIn) next() else next('/login') }) router.beforeEach((to) => { // ✅ 这样做是可行的,因为路由器是在其被安装之后开始导航的, // 而此时 Pinia 也已经被安装。 const store = useStore() if (to.meta.requiresAuth && !store.isLoggedIn) return '/login' }) ``` -------------------------------- ### _Awaited Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Utility type to get the awaited type of a Promise. ```APIDOC Ƭ **_Awaited** <`T`>: `T` extends `null` | `undefined` ? `T` : `T` extends `object` & { `then`: (`onfulfilled`: `F`) => `any` } ? `F` extends (`value`: infer V, ...`args`: `any`) => `any` ? `_Awaited`<`V`> : `never` : `T` #### 类型参数 名称 --- `T` ``` -------------------------------- ### getActivePinia Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Gets the currently active Pinia instance, if one exists. ```APIDOC ## getActivePinia ### Description Gets the currently active Pinia instance, if one exists. ### Returns `undefined` | `Pinia` ``` -------------------------------- ### Set Initial State for Stores in Tests Source: https://pinia.vuejs.org/zh/cookbook/testing.html Configure the initial state for all stores during testing by passing an `initialState` object to `createTestingPinia`. This object should mirror the store's structure, using the store's name as the key. ```typescript // 在测试中的某处 const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ initialState: { counter: { n: 20 }, //从 20 开始计数,而不是 0 }, }), ], }, }) const store = useSomeStore() // 使用 pinia 的测试实例! store.n // 20 ``` -------------------------------- ### StoreGetters Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Extracts the getters from a store type, compatible with both Setup and Option Stores. ```APIDOC Ƭ **StoreGetters** <`SS`>: `SS` extends `Store`<`string`, `StateTree`, `infer G`, `_ActionsTree`> ? `_StoreWithGetters`<`G`> : `_ExtractGettersFromSetupStore`<`SS`> 提取一个 store 类型的 getter。 对 Setup Store 或 Option Store都有效。 #### 类型参数 名称 --- `SS` ``` -------------------------------- ### StoreState Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Extracts the state from a store type, automatically unwrapping refs. Compatible with Setup and Option Stores. ```APIDOC Ƭ **StoreState** <`SS`>: `SS` extends `Store`<`string`, `infer S`, `_GettersTree`<`StateTree`>, `_ActionsTree`> ? `UnwrapRef`<`S`> : `_ExtractStateFromSetupStore`<`SS`> 提取一个 store 类型的 state。 对 Setup Store 或 Option Store 都有效。请注意,它自动解包 refs。 #### 类型参数 名称 --- `SS` ``` -------------------------------- ### Component Unit Test with createTestingPinia Source: https://pinia.vuejs.org/zh/cookbook/testing.html Use `createTestingPinia` from `@pinia/testing` to mount components with a dedicated `pinia` instance. This allows direct state manipulation and testing of actions. ```shell npm i -D @pinia/testing ``` ```javascript import { mount } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' // 引入任何你想要测试的 store import { useSomeStore } from '@/stores/myStore' const wrapper = mount(Counter, { global: { plugins: [createTestingPinia()], }, }) const store = useSomeStore() // // 使用 pinia 的测试实例! // 可直接操作 state store.name = 'my new name' // 也可以通过 patch 来完成 store.$patch({ name: 'new name' }) expect(store.name).toBe('new name') // action 默认是存根的(stubbed),意味着它们默认不执行其代码。 // 请看下面的内容来定制这一行为。 store.someAction() expect(store.someAction).toHaveBeenCalledTimes(1) expect(store.someAction).toHaveBeenLastCalledWith() ``` -------------------------------- ### Using Vuex store in router guards Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Example of accessing Vuex getters within a Vue Router navigation guard. ```typescript import vuexStore from '@/store' router.beforeEach((to, from, next) => { if (vuexStore.getters['auth/user/loggedIn']) next() else next('/login') }) ``` -------------------------------- ### Refactor Vuex Modules to Pinia Stores Source: https://pinia.vuejs.org/zh/cookbook/migration-vuex.html Compares the directory structure of a Vuex store with multiple modules to an equivalent Pinia store structure. Note how module IDs in Vuex map to store IDs in Pinia. ```bash # Vuex example (assuming named modules). src └── store ├── index.js # Initialize Vuex, import modules └── modules ├── module1.js # Named module 'module1' └── nested ├── index.js # Named module 'nested', import module2 and module3 ├── module2.js # Named module 'nested/module2' └── module3.js # Named module 'nested/module3' # Pinia example, note the IDs matching the previous named modules src └── stores ├── index.js # (Optional) Initialize Pinia, no need to import stores ├── module1.js # 'module1' id ├── nested-module2.js # 'nested/module2' id ├── nested-module3.js # 'nested/module3' id └── nested.js # 'nested' id ``` -------------------------------- ### Define a Basic Pinia Store Source: https://pinia.vuejs.org/zh/core-concepts Define a Pinia store using `defineStore()`. The first argument is a unique name for the store, which is also used as its ID for devtools. The returned function should ideally be named `use...Store`. ```js import { defineStore } from 'pinia' // `defineStore()` 的返回值的命名是自由的 // 但最好含有 store 的名字,且以 `use` 开头,以 `use` 结尾。 // (比如 `useUserStore`,`useCartStore`,`useProductStore`) // 第一个参数是你的应用中 Store 的唯一 ID。 export const useAlertsStore = defineStore('alerts', { // 其他配置... }) ``` -------------------------------- ### Update plugin DefineStoreOptions type Source: https://pinia.vuejs.org/zh/cookbook/migration-v1-v2.html If you extend `DefineStoreOptions` in your TypeScript plugins, rename it to `DefineStoreOptionsBase`. This type works for both setup and option stores. ```typescript declare module 'pinia' { export interface DefineStoreOptions<S, Store> { export interface DefineStoreOptionsBase<S, Store> { debounce?: { [k in keyof StoreActions<Store>]?: number } } } ``` -------------------------------- ### createTestingPinia Source: https://pinia.vuejs.org/zh/api/modules/pinia_testing.html Creates a Pinia instance designed for unit testing. It requires mocking stores and defaults to mocking all actions to prevent their execution, facilitating isolated testing. You can customize action mocking behavior using the `stubActions` option or provide a custom `createSpy` function. ```APIDOC ## createTestingPinia ### Description Creates a Pinia instance for unit testing, requiring store mocking. By default, all actions are mocked and will not execute, enabling isolated unit testing of stores and components. This behavior can be modified with the `stubActions` option, or by providing a custom `createSpy` function if not using Jest. ### Signature ▸ createTestingPinia(options?): TestingPinia ### Parameters #### Options - **options** (TestingOptions) - Optional - Configuration options for the Pinia testing instance. ### Returns - **TestingPinia** - An enhanced Pinia instance for testing. ``` -------------------------------- ### Upgrade to Pinia v2 Source: https://pinia.vuejs.org/zh/cookbook/migration-v1-v2.html Upgrade to Pinia v2 using npm or yarn. This command will install the latest stable version of Pinia v2. ```shell npm i 'pinia@^2.x.x' # 或者使用 yarn yarn add 'pinia@^2.x.x' ``` -------------------------------- ### Get Active Pinia Instance Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Retrieve the currently active Pinia instance if one is available. Returns `undefined` if no Pinia instance is active. ```typescript getActivePinia() ``` -------------------------------- ### Adding and Using Typed Store Properties Source: https://pinia.vuejs.org/zh/core-concepts/plugins.html Demonstrates how to assign values to the custom properties ('hello', 'simpleNumber') defined in PiniaCustomProperties within a plugin. ```typescript pinia.use(({ store }) => { store.hello = 'Hola' store.hello = ref('Hola') store.simpleNumber = Math.random() // @ts-expect-error: we haven't typed this correctly store.simpleNumber = ref(Math.random()) }) ``` -------------------------------- ### skipHydrate Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Tells Pinia to skip the hydration process for a given object. Useful when returning a stateful object from a setup store that isn't actually state. ```APIDOC ## skipHydrate ### Description Tells Pinia to skip the hydration process for a given object. This is useful when returning a stateful object from a setup store that isn't actually state. For example, returning a router instance in a setup store. ### Method `skipHydrate<T>(obj: T): T` ### Type Parameters - **T**: `any` - The type of the object to skip hydration for. ### Parameters #### Path Parameters - **obj** (T) - Required - The target object to skip hydration for. ``` -------------------------------- ### mapGetters Source: https://pinia.vuejs.org/zh/api/modules/pinia.html Generates an object to be passed to the `computed` field of a component, allowing the use of state and getters from a store without using the Composition API (`setup()`). ```APIDOC ## mapGetters ### Description Generates an object to be passed to the `computed` field of a component, allowing the use of state and getters from a store without using the Composition API (`setup()`). ### Method Signature `mapGetters<Id, S, G, A, Keys>(useStore, keys): _MapStateReturn<S, G, Keys>` ### Parameters #### `useStore` - **Type**: `StoreDefinition<Id, S, G, A>` - **Description**: The store definition to map from. #### `keys` - **Type**: `readonly Keys[]` - **Description**: An array of state properties or getters to map. ### Returns - **Type**: `_MapStateReturn<S, G, Keys>` ### Example ```javascript export default { computed: { // Other computed properties ...mapGetters(useCounterStore, ['count', 'double']) }, created() { this.count // 2 this.double // 4 } } ``` ``` -------------------------------- ### Call Actions in Vue Component Source: https://pinia.vuejs.org/zh/core-concepts/actions.html Actions can be called directly as methods on the store instance within the `<script setup>` block or even directly in the template. ```vue <script setup> const store = useCounterStore() // 将 action 作为 store 的方法进行调用 store.randomizeCounter() </script> <template> <!-- 即使在模板中也可以 --> <button @click="store.randomizeCounter()">Randomize</button> </template> ```