### 在 Setup Store 中使用 inject Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/core-concepts/index.md Setup Store 可以依赖全局提供的属性,使用 inject 访问。 ```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 { // ... } }) ``` -------------------------------- ### Install @pinia/testing Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Install the testing package as a development dependency. ```shell npm i -D @pinia/testing ``` -------------------------------- ### Using stores in setup Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/index.md Stores work automatically when called at the top level of setup functions. ```vue ``` -------------------------------- ### Setup Store Internal Action Call Example Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Illustrates a limitation in Setup stores where internal action calls using closed-over function references bypass stubs. Routing calls through the store instance (`store.increment()`) ensures stubs are used. ```js // Setup store export const useCounterStore = defineStore('counter', () => { function increment() { /* ... */ } function incrementTwice() { const store = useCounterStore() store.increment() // ✅ goes through the store, uses the stub store.increment() } return { increment, incrementTwice } }) ``` -------------------------------- ### Define custom options with setup syntax Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/plugins.md Pass custom options as the third argument when using the setup syntax for store definition. ```js defineStore( 'search', () => { // ... }, { // this will be read by a plugin later on debounce: { // debounce the action searchContacts by 300ms searchContacts: 300, }, } ) ``` -------------------------------- ### Install Pinia Source: https://github.com/vuejs/pinia/blob/v4/README.md Use npm, pnpm, or yarn to install Pinia. This command is for npm. ```bash npm install pinia ``` -------------------------------- ### Using Actions in Options API with `setup()` Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/actions.md When using the Options API with the `setup()` hook, you can access store actions directly via the `setup` function's returned store instance, allowing methods to call them. ```vue ``` -------------------------------- ### Define a Setup Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/index.md Setup stores use a function to define reactive properties and methods, similar to the 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 } }) ``` -------------------------------- ### Install Pinia with deno Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Install Pinia using deno. Deno is a secure runtime for JavaScript and TypeScript. ```bash deno add pinia ``` -------------------------------- ### Install Pinia with Yarn or npm Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/getting-started.md Use your preferred package manager to install Pinia. If using Vue 2, you'll also need to install `@vue/composition-api`. ```bash yarn add pinia # or npm npm install pinia ``` -------------------------------- ### Define a Pinia store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-0-0-7.md Example store definition used for migration examples. ```js const useStore({ id: 'main', state: () => ({ count: 0 }) }) ``` -------------------------------- ### Install Pinia with bun Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Install Pinia using bun. Bun is a new, fast JavaScript runtime, bundler, transpiler, and package manager. ```bash bun add pinia ``` -------------------------------- ### Use composables in Setup Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/composables.md Setup stores allow using almost any composable, as properties are automatically discerned into state, actions, or getters. ```ts import { defineStore } from 'pinia' import { useMediaControls } from '@vueuse/core' export const useVideoPlayer = defineStore('video', () => { // we won't expose (return) this element directly const videoElement = ref() const src = ref('/data/video.mp4') const { playing, volume, currentTime, togglePictureInPicture } = useMediaControls(videoElement, { src }) function loadVideo(element: HTMLVideoElement, src: string) { videoElement.value = element src.value = src } return { src, playing, volume, currentTime, loadVideo, togglePictureInPicture, } }) ``` -------------------------------- ### Access Pinia Getters in Options API with setup() Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/getters.md Use the `setup()` hook in the Options API to access Pinia stores. This method is recommended for migrating components but should not be mixed with Composition API styles long-term. ```vue ``` -------------------------------- ### Install Pinia for Nuxt 2 without Bridge Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/ssr/nuxt.md Install Pinia, the Nuxt module, and the composition API module for Nuxt 2. Ensure you are using version '@pinia/nuxt@0.2.1' or earlier for this setup. ```bash yarn add pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api # 使用 npm npm install pinia @pinia/nuxt@0.2.1 @nuxtjs/composition-api ``` -------------------------------- ### Install Pinia with pnpm Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Install Pinia using pnpm. pnpm is a fast, disk-space-efficient package manager. ```bash pnpm add pinia ``` -------------------------------- ### Install Pinia 0.x for Deprecation Checks Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-v1-v2.md Install the latest 0.x version to identify deprecated APIs before upgrading. ```shell npm i 'pinia@^0.x.x' # or with yarn yarn add 'pinia@^0.x.x' ``` -------------------------------- ### Compose Stores in Setup Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/cookbook/composing-stores.md Import and use another store at the top level of a setup store function. ```ts 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 } }) ``` -------------------------------- ### Create and Install Pinia Instance (Vue 2) Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/cookbook/migration-0-0-7.md For Vue 2 applications, create a Pinia instance using `createPinia()` and install the `PiniaVuePlugin` before mounting your Vue app. ```javascript import Vue from 'vue' import { createPinia, PiniaVuePlugin } from 'pinia' const pinia = createPinia() Vue.use(PiniaVuePlugin) new Vue({ el: '#app', pinia, // ... }) ``` -------------------------------- ### Initialize Pinia instance Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-0-0-7.md Create and install a Pinia instance for Vue 2 or Vue 3 applications. ```js import Vue from 'vue' import { createPinia, PiniaVuePlugin } from 'pinia' const pinia = createPinia() Vue.use(PiniaVuePlugin) new Vue({ el: '#app', pinia, // ... }) ``` ```js import { createApp } from 'vue' import { createPinia, PiniaVuePlugin } from 'pinia' import App from './App.vue' const pinia = createPinia() createApp(App).use(pinia).mount('#app') ``` -------------------------------- ### Initialize and Run Vite Vue Project Source: https://github.com/vuejs/pinia/blob/v4/packages/online-playground/src/download/template/README.md Commands to install project dependencies and launch the development server using npm or yarn. ```sh npm install npm run dev # if using yarn: yarn yarn dev ``` -------------------------------- ### 在组件中使用 Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/core-concepts/index.md 在组件的 setup 中调用 useStore 函数来实例化 Store。 ```vue ``` -------------------------------- ### Install Pinia with yarn Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Install Pinia using yarn. Yarn is another popular package manager for JavaScript. ```bash yarn add pinia ``` -------------------------------- ### Install Pinia with npm Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Install Pinia using npm. This is the most common package manager for Node.js projects. ```bash npm install pinia ``` -------------------------------- ### Add Static Property to All Stores with a Plugin Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/plugins.md This example demonstrates how to add a static property 'secret' to every store created after the plugin is installed. This is useful for global objects like router or modal managers. ```javascript import { createPinia } from 'pinia' // add a property named `secret` to every store that is created // after this plugin is installed this could be in a different file function SecretPiniaPlugin() { return { secret: 'the cake is a lie' } } const pinia = createPinia() // give the plugin to pinia pinia.use(SecretPiniaPlugin) // in another file const store = useStore() store.secret // 'the cake is a lie' ``` -------------------------------- ### Implementing $reset() in Setup Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/state.md For Setup Stores, define a custom $reset() method within the store to reset state properties. ```typescript export const useCounterStore = defineStore('counter', () => { const count = ref(0) function $reset() { count.value = 0 } return { count, $reset } }) ``` -------------------------------- ### Create and Install Pinia Instance (Vue 3) Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/cookbook/migration-0-0-7.md For Vue 3 applications, create a Pinia instance using `createPinia()` and use it with `createApp().use(pinia)` before mounting your Vue app. ```javascript import { createApp } from 'vue' import { createPinia, PiniaVuePlugin } from 'pinia' import App from './App.vue' const pinia = createPinia() createApp(App).use(pinia).mount('#app') ``` -------------------------------- ### Define a Pinia Store with Getters Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/getters.md This is an example of how to define a Pinia store with state and getters. It serves as a prerequisite for the examples demonstrating getter usage in components. ```javascript import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), getters: { doubleCount(state) { return state.count * 2 }, }, }) ``` -------------------------------- ### Install Pinia Nuxt module Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/nuxt.md Use the Nuxt CLI to add the Pinia module to your project. ```bash npx nuxi@latest module add pinia ``` -------------------------------- ### Access global properties in a Setup Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/index.md Setup stores can access globally provided properties using inject or other composables. ```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 { // ... } }) ``` -------------------------------- ### Create and Use Pinia Instance Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/getting-started.md Create a Pinia instance and pass it to the Vue app as a plugin. This is the root store setup. ```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') ``` -------------------------------- ### Install Pinia Plugin in Vue 3 Source: https://github.com/vuejs/pinia/blob/v4/README.md Installs the Pinia plugin by creating a Pinia instance and passing it to the Vue application. Ensure this is done before mounting the app. ```js 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') ``` -------------------------------- ### Mapping Actions in Options API without `setup()` Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/actions.md Use the `mapActions` helper from Pinia to map store actions as methods directly onto the component's `methods` object when not using the `setup()` hook. ```javascript import { mapActions } from 'pinia' import { useCounterStore } from '../stores/counter' export default { methods: { // gives access to this.increment() inside the component // same as calling from store.increment() ...mapActions(useCounterStore, ['increment']), // same as above but registers it as this.myOwnName() ...mapActions(useCounterStore, { myOwnName: 'increment' }), }, } ``` -------------------------------- ### Run SFC Playground Locally Source: https://github.com/vuejs/pinia/blob/v4/packages/online-playground/README.md Execute this command in the repository root to start the SFC Playground in development mode. ```sh pnpm dev-sfc ``` -------------------------------- ### Install Pinia with Nuxt Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/ssr/nuxt.md Install Pinia and the Nuxt module using yarn or npm. If you encounter dependency resolution issues with npm, add the 'overrides' section to your package.json. ```bash yarn add pinia @pinia/nuxt # 或者使用 npm npm install pinia @pinia/nuxt ``` ```json "overrides": { "vue": "latest" } ``` -------------------------------- ### Install Pinia for Nuxt Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-v1-v2.md Commands to add the Pinia Nuxt package using npm or yarn. ```bash npm i @pinia/nuxt # or with yarn yarn add @pinia/nuxt ``` -------------------------------- ### Access Getter in Setup Script Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/getters.md Demonstrates direct access to getters as properties of the store instance within a Vue component's ` ``` -------------------------------- ### Integrating Pinia Plugins in Tests Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Shows how to install and use Pinia plugins during testing by creating a Pinia instance with the plugin and then using it with a Vue application instance. This is necessary because plugins are only active when Pinia is installed in an App. ```js import { setActivePinia, createPinia } from 'pinia' import { createApp } from 'vue' import { somePlugin } from '../src/stores/plugin' // same code as above... // you don't need to create one app per test const app = createApp({}) beforeEach(() => { const pinia = createPinia().use(somePlugin) app.use(pinia) setActivePinia(pinia) }) ``` -------------------------------- ### Accessing stores outside of setup Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/index.md Pass the pinia instance to the store function when working outside of the component setup context. ```js const pinia = createPinia() const app = createApp(App) app.use(router) app.use(pinia) router.beforeEach((to) => { // ✅ This will work make sure the correct store is used for the // current running app const main = useMainStore(pinia) if (to.meta.requiresAuth && !main.isLoggedIn) return '/login' }) ``` -------------------------------- ### Avoid Circular Dependencies in Setup Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/cookbook/composing-stores.md Do not read state from another store directly in the setup function body if it creates a circular dependency. Access state within computed properties or actions instead. ```js const useX = defineStore('x', () => { const y = useY() // ❌ 这是不可以的,因为 y 也试图读取 x.name y.name function doSomething() { // ✅ 读取 computed 或 action 中的 y 属性 const yName = y.name // ... } return { name: ref('I am X'), } }) const useY = defineStore('y', () => { const x = useX() // ❌ 这是不可以的,因为 x 也试图读取 y.name x.name function doSomething() { // ✅ 读取 computed 或 action 中的 x 属性 const xName = x.name // ... } return { name: ref('I am Y'), } }) ``` -------------------------------- ### Define Store for Options API Example Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/actions.md This is a basic Pinia store definition used in subsequent Options API examples. It includes a state property and a simple increment action. ```javascript // Example File Path: // ./src/stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), actions: { increment() { this.count++ }, }, }) ``` -------------------------------- ### Use Store Outside Setup in Nuxt Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/ssr/nuxt.md When using a store outside the `setup()` function in Nuxt, pass the `pinia` object to `useStore()`. This allows access within `asyncData()` and `fetch()`. ```javascript import { useStore } from '~/stores/myStore' export default { asyncData({ $pinia }) { const store = useStore($pinia) }, } ``` ```vue ``` -------------------------------- ### Upgrade to Pinia 2.x Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-v1-v2.md Install the latest 2.x version to begin the migration process. ```shell npm i 'pinia@^2.x.x' # or with yarn yarn add 'pinia@^2.x.x' ``` -------------------------------- ### Instantiate a Pinia Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/index.md Import and use the store within a component's script setup. The store is created when `use...Store()` is called. ```vue ``` -------------------------------- ### Define a Pinia Store with Types in JavaScript Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/introduction.md This example demonstrates how to define a Pinia store using JavaScript while leveraging TypeScript type annotations for state, getters, and actions. It includes examples of state management, computed properties (getters), and asynchronous operations (actions). ```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', // type will be automatically inferred to number nextId: 0, }), getters: { finishedTodos(state) { // autocompletion! ✨ 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') { // call other getters with autocompletion ✨ return this.finishedTodos } else if (this.filter === 'unfinished') { return this.unfinishedTodos } return this.todos }, }, actions: { // any amount of arguments, return a promise or not addTodo(text) { // you can directly mutate the state this.todos.push({ text, id: this.nextId++, isFinished: false }) }, }, }) ``` -------------------------------- ### Using Nested Stores in Setup Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/composing-stores.md When using setup stores, you can import and call other store's `useStore()` function directly at the top of the store function. This allows you to interact with the nested store as you would from a Vue component. ```ts import { defineStore } from 'pinia' import { useUserStore } from './user' import { apiPurchase } from './api' 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, list.value) } return { list, summary, purchase } }) ``` -------------------------------- ### Skip hydration in Setup Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/composables.md Use the skipHydrate helper in Setup stores to prevent specific state properties from being picked up from the initial SSR state. ```ts import { defineStore, skipHydrate } from 'pinia' import { useEyeDropper, useLocalStorage } from '@vueuse/core' export const useColorStore = defineStore('colors', () => { const { isSupported, open, sRGBHex } = useEyeDropper() const lastColor = useLocalStorage('lastColor', sRGBHex) // ... return { lastColor: skipHydrate(lastColor), // Ref open, // Function isSupported, // boolean (not even reactive) } }) ``` -------------------------------- ### Vuex Module Example Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-vuex.md This is a standard Vuex module with namespaced state, getters, actions, and mutations. ```typescript // Vuex module in the 'auth/user' namespace import { Module } from 'vuex' import { api } from '@/api' import { RootState } from '@/types' // if using a Vuex type definition interface State { firstName: string lastName: string userId: number | null } const storeModule: Module = { namespaced: true, state: { firstName: '', lastName: '', userId: null, }, getters: { firstName: (state) => state.firstName, fullName: (state) => `${state.firstName} ${state.lastName}`, loggedIn: (state) => state.userId !== null, // combine with some state from other modules fullUserDetails: (state, getters, rootState, rootGetters) => { return { ...state, fullName: getters.fullName, // read the state from another module named `auth` ...rootState.auth.preferences, // read a getter from a namespaced module called `email` nested under `auth` ...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 stores in onServerPrefetch Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/index.md No special configuration is required when using onServerPrefetch within setup. ```vue ``` -------------------------------- ### Define Store Using a Function Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/introduction.md An alternative way to define a Pinia store using a function, similar to Vue's `setup()` function. This approach is useful for more complex store logic. ```javascript import { defineStore } from 'pinia' import { ref } from 'vue' export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value++ } return { count, increment } }) ``` -------------------------------- ### Create and Use Root Pinia Instance (Vue 2) Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/getting-started.md For Vue 2 applications, install the `PiniaVuePlugin` and use the `pinia` instance with your Vue application. Note that the same `pinia` instance can be used across multiple Vue applications on the same page. ```javascript import { createPinia, PiniaVuePlugin } from 'pinia' Vue.use(PiniaVuePlugin) const pinia = createPinia() new Vue({ el: '#app', // other options... // ... // Note that the same `pinia` instance // can be used in multiple Vue instances on the same page. pinia, }) ``` -------------------------------- ### Define a Basic Counter Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/state.md Example of defining a Pinia store using the Options API with a state property. ```javascript // Example File Path: // ./src/stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, }), }) ``` -------------------------------- ### Pinia Store VS Code Snippets Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/vscode-snippets.md JSON configuration for VS Code user snippets to bootstrap Pinia Options and Setup stores. ```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" } } ``` -------------------------------- ### Accessing stores in SPAs Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/outside-component-usage.md Demonstrates the importance of calling useStore only after the pinia instance has been created and installed via app.use(pinia). ```js import { useUserStore } from '@/stores/user' import { createPinia } from 'pinia' import { createApp } from 'vue' import App from './App.vue' // ❌ fails because it's called before the pinia is created const userStore = useUserStore() const pinia = createPinia() const app = createApp(App) app.use(pinia) // ✅ works because the pinia instance is now active const userStore = useUserStore() ``` -------------------------------- ### Configure npm dependency overrides Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/nuxt.md Add this to package.json if you encounter ERESOLVE dependency tree errors during installation. ```js "overrides": { "vue": "latest" } ``` -------------------------------- ### Install Pinia Dependencies Manually Source: https://github.com/vuejs/pinia/blob/v4/packages/nuxt/README.md Manually add Pinia and the Pinia Nuxt module as dependencies to your Nuxt project. ```shell npm i pinia @pinia/nuxt ``` -------------------------------- ### Calling Actions in Vue Components Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/actions.md Actions are invoked like regular functions or methods directly on the store instance, both in the ` ``` -------------------------------- ### Vuex vs Pinia Directory Structure Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-vuex.md Compares the directory structure of Vuex modules with their Pinia store equivalents, highlighting how module namespaces map to store IDs. ```bash # Vuex example (assuming namespaced modules) src └── store ├── index.js # Initializes Vuex, imports modules └── modules ├── module1.js # 'module1' namespace └── nested ├── index.js # 'nested' namespace, imports module2 & module3 ├── module2.js # 'nested/module2' namespace └── module3.js # 'nested/module3' namespace # Pinia equivalent, note ids match previous namespaces src └── stores ├── index.js # (Optional) Initializes Pinia, does not import stores ├── module1.js # 'module1' id ├── nested-module2.js # 'nestedModule2' id ├── nested-module3.js # 'nestedModule3' id └── nested.js # 'nested' id ``` -------------------------------- ### Build SFC Playground for Production Source: https://github.com/vuejs/pinia/blob/v4/packages/online-playground/README.md Run this command in the repository root to build the SFC Playground for a production deployment. ```sh pnpm build-sfc-playground ``` -------------------------------- ### 重构 Vuex 模块结构为 Pinia stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/cookbook/migration-vuex.md 展示了从嵌套的 Vuex 模块目录结构到扁平化 Pinia store 目录结构的映射方式。 ```bash # Vuex 示例(假设是命名模块)。 src └── store ├── index.js # 初始化 Vuex,导入模块 └── modules ├── module1.js # 命名模块 'module1' └── nested ├── index.js # 命名模块 'nested',导入 module2 与 module3 ├── module2.js # 命名模块 'nested/module2' └── module3.js # 命名模块 'nested/module3' # Pinia 示例,注意 ID 与之前的命名模块相匹配 src └── stores ├── index.js # (可选) 初始化 Pinia,不必导入 store ├── module1.js # 'module1' id ├── nested-module2.js # 'nested/module2' id ├── nested-module3.js # 'nested/module3' id └── nested.js # 'nested' id ``` -------------------------------- ### Use Pinia Store in Vue Component Setup Source: https://github.com/vuejs/pinia/blob/v4/README.md Accesses a Pinia store within a Vue component's setup function. Use `storeToRefs` to destructure state and getters reactively. ```ts import { useMainStore } from '@/stores/main' import { storeToRefs } from 'pinia' export default defineComponent({ setup() { const main = useMainStore() // extract specific store properties const { counter, doubleCounter } = storeToRefs(main) return { // gives access to the whole store in the template main, // gives access only to specific state or getter counter, doubleCounter, } }, }) ``` -------------------------------- ### Unit Testing a Pinia Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Demonstrates how to unit test a Pinia store by creating and activating a fresh Pinia instance before each test. This ensures a clean state for testing store actions and properties. ```js import { setActivePinia, createPinia } from 'pinia' import { useCounterStore } from '../src/stores/counter' describe('Counter Store', () => { beforeEach(() => { // creates a fresh pinia and makes it active // so it's automatically picked up by any useStore() call // without having to pass it to it: `useStore(pinia)` setActivePinia(createPinia()) }) it('increments', () => { const counter = useCounterStore() expect(counter.n).toBe(0) counter.increment() expect(counter.n).toBe(1) }) it('increments by amount', () => { const counter = useCounterStore() counter.increment(10) expect(counter.n).toBe(10) }) }) ``` -------------------------------- ### Define a Counter Store Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/introduction.md Use `defineStore` to create a Pinia store with state and actions. This is the standard way to define a store for managing application state. ```javascript import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => { return { count: 0 } }, // 也可以这样定义 // state: () => ({ count: 0 }) actions: { increment() { this.count++ }, }, }) ``` -------------------------------- ### Using stores in serverPrefetch Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/index.md Access the pinia instance via this.$pinia within serverPrefetch. ```js export default { serverPrefetch() { const store = useStore(this.$pinia) }, } ``` -------------------------------- ### Define a Pinia Store with Composition API Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/introduction.md Define a store using the Composition API with `ref` for state and regular functions for actions. Return the state and actions to make them available. ```javascript export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value++ } return { count, increment } }) ``` -------------------------------- ### Add Options to Store via Plugin Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/plugins.md Injects initial options into the store instance. ```ts pinia.use(({ options }) => ({ $options: options })) ``` -------------------------------- ### Use store outside setup context Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/ssr/nuxt.md Pass the pinia instance explicitly when using stores in contexts like middleware or navigation guards. ```ts import { useStore } from '~/stores/myStore' // this line is usually inside a function that is able to retrieve // the pinia instance const store = useStore(pinia) ``` -------------------------------- ### Set Initial Application State (SSR Hydration) Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/state.md Set the initial state of the entire application by modifying the `state` of the `pinia` instance. This is crucial for SSR hydration. ```javascript pinia.state.value = {} ``` -------------------------------- ### Configure Webpack for .mjs Files Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-v1-v2.md Update webpack configuration to handle .mjs files correctly when using older versions of Vue CLI or manual webpack setups. ```javascript // vue.config.js module.exports = { configureWebpack: { module: { rules: [ { test: /\.mjs$/, include: /node_modules/, type: 'javascript/auto', }, ], }, }, } ``` ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /\.mjs$/, include: /node_modules/, type: 'javascript/auto', }, ], }, } ``` -------------------------------- ### Applying Pinia Plugins in Testing Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md When using `createTestingPinia`, ensure any Pinia plugins are passed directly to the `plugins` option. Avoid using `testingPinia.use()` after creation. ```javascript import { createTestingPinia } from '@pinia/testing' import { somePlugin } from '../src/stores/plugin' // inside some test const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ stubActions: false, plugins: [somePlugin], }), ], }, }) ``` -------------------------------- ### Cache Results Inside Getter Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/getters.md Provides an example of caching results within a getter itself for performance, which is an alternative to the standard getter caching mechanism when dealing with complex computations or filtering. ```javascript export const useStore = defineStore('main', { getters: { getActiveUserById(state) { const activeUsers = state.users.filter((user) => user.active) return (userId) => activeUsers.find((user) => user.id === userId) }, }, }) ``` -------------------------------- ### Accessing Other Stores' Actions Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/actions.md You can use other stores directly within an action by calling `useStoreName()` inside the action function. This example shows fetching user preferences only if the user is authenticated. ```javascript import { useAuthStore } from './auth-store' export const useSettingsStore = defineStore('settings', { state: () => ({ preferences: null, // ... }), actions: { async fetchUserPreferences() { const auth = useAuthStore() if (auth.isAuthenticated) { this.preferences = await fetchPreferences() } else { throw new Error('User must be authenticated') } }, }, }) ``` -------------------------------- ### Return a Function from Getter to Accept Arguments Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/getters.md Shows how to return a function from a getter to simulate passing arguments. Note that this bypasses caching, making the getter behave like a regular function call. ```javascript export const useStore = defineStore('main', { getters: { getUserById: (state) => { return (userId) => state.users.find((user) => user.id === userId) }, }, }) ``` -------------------------------- ### Convert Vuex Component Usage to Pinia Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/migration-vuex.md Update component setup to import and use Pinia stores instead of Vuex. Access state and getters directly from the Pinia store instance. ```typescript // Vuex 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, } }, }) ``` ```typescript // Pinia 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 { // you can also access the whole store in your component by returning it authUserStore, firstName, fullName, } }, }) ``` -------------------------------- ### Avoid Infinite Loops When Composing Stores Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/composing-stores.md When two or more stores use each other, they must not create an infinite loop through getters or actions. Specifically, they cannot both directly read each other's state in their setup function. ```js const useX = defineStore('x', () => { const y = useY() // ❌ This is not possible because y also tries to read x.name y.name function doSomething() { // ✅ Read y properties in computed or actions const yName = y.name // ... } return { name: ref('I am X'), } }) const useY = defineStore('y', () => { const x = useX() // ❌ This is not possible because x also tries to read y.name x.name function doSomething() { // ✅ Read x properties in computed or actions const xName = x.name // ... } return { name: ref('I am Y'), } }) ``` -------------------------------- ### Initialize Store State with Testing Pinia Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Set the initial state for all stores when creating a testing Pinia instance by providing an `initialState` object. The keys in this object should match the store names. ```ts // somewhere in your test const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ initialState: { counter: { n: 20 }, // start the counter at 20 instead of 0 }, }), ], }, }) const store = useSomeStore() // uses the testing pinia! store.n // 20 ``` -------------------------------- ### Execute Actions with stubActions: false Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/cookbook/testing.md Configure `createTestingPinia` with `stubActions: false` to allow store actions to execute their actual implementation during tests. Actions are still wrapped with spies for inspection. ```js const wrapper = mount(Counter, { global: { plugins: [createTestingPinia({ stubActions: false })], }, }) const store = useSomeStore() // Now this call WILL execute the implementation defined by the store store.someAction() // ...but it's still wrapped with a spy, so you can inspect calls expect(store.someAction).toHaveBeenCalledTimes(1) ``` -------------------------------- ### Using stores in navigation guards Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/core-concepts/outside-component-usage.md Shows how to defer store access within Vue Router navigation guards to ensure the pinia instance is available. ```js import { createRouter } from 'vue-router' const router = createRouter({ // ... }) // ❌ Depending on the order of imports this will fail const store = useUserStore() router.beforeEach((to, from, next) => { // we wanted to use the store here if (store.isLoggedIn) next() else next('/login') }) router.beforeEach((to) => { // ✅ This will work because the router starts its navigation after // the router is installed and pinia will be installed too const store = useUserStore() if (to.meta.requiresAuth && !store.isLoggedIn) return '/login' }) ``` -------------------------------- ### Use Counter Store in a Vue Component Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/introduction.md Import and use the defined store within a Vue component's script setup. Access state directly and call actions to modify the state. ```vue ``` -------------------------------- ### Use a Pinia Store in a Vue Component Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/introduction.md Import and use a Pinia store within a Vue component's script setup. Access state directly or use actions and $patch for state modifications. ```vue ``` -------------------------------- ### 监听 Action 执行过程 Source: https://github.com/vuejs/pinia/blob/v4/packages/docs/zh/core-concepts/actions.md 使用 $onAction 监听 action 的调用,并通过 after 和 onError 钩子处理执行结果或错误。 ```js const unsubscribe = someStore.$onAction( ({ name, // action 名称 store, // store 实例,类似 `someStore` args, // 传递给 action 的参数数组 after, // 在 action 返回或解决后的钩子 onError, // action 抛出或拒绝的钩子 }) => { // 为这个特定的 action 调用提供一个共享变量 const startTime = Date.now() // 这将在执行 "store "的 action 之前触发。 console.log(`Start "${name}" with params [${args.join(', ')}].`) // 这将在 action 成功并完全运行后触发。 // 它等待着任何返回的 promise after((result) => { console.log( `Finished "${name}" after ${ Date.now() - startTime }ms.\nResult: ${result}.` ) }) // 如果 action 抛出或返回一个拒绝的 promise,这将触发 onError((error) => { console.warn( `Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.` ) }) } ) // 手动删除监听器 unsubscribe() ```