Skip to content

Pinia 实例

Pinia 实例是管理应用程序中所有 store 的中央枢纽。它通过 createPinia() 创建并作为 Vue 插件安装。

创建 Pinia 实例

createPinia()

创建一个新的 Pinia 实例。

签名

ts
function createPinia(): Pinia

示例

js
import { createPinia } from 'pinia'
import { createApp } from 'vue'

const app = createApp({})
const pinia = createPinia()

app.use(pinia)

属性

install

  • 类型: (app: App, ...options: any[]) => any

Vue 插件安装函数。使用 app.use(pinia) 时自动调用。

state

  • 类型: Ref<Record<string, StateTree>>
  • 只读

包含所有 store 状态的响应式对象。主要用于内部和开发工具。

js
const pinia = createPinia()
console.log(pinia.state.value) // { user: { ... }, cart: { ... } }

方法

use()

向 Pinia 实例添加插件。插件会应用到添加插件后创建的所有 store。

签名

ts
use(plugin: PiniaPlugin): Pinia

参数

  • plugin: 接收上下文对象并可选择性返回要添加到 store 的属性的函数

示例

js
import { createPinia } from 'pinia'

const pinia = createPinia()

// 添加简单插件
pinia.use(({ store }) => {
  store.hello = 'world'
})

// 添加带选项的插件
function createLoggerPlugin(options = {}) {
  return ({ store }) => {
    if (options.enabled) {
      store.$onAction(({ name, args }) => {
        console.log(`Action ${name} 被调用,参数:`, args)
      })
    }
  }
}

pinia.use(createLoggerPlugin({ enabled: true }))

_s (内部)

  • 类型: Map<string, StoreGeneric>
  • 仅供内部使用

包含所有已注册 store 的 Map。由 Pinia 内部使用。

_e (内部)

  • 类型: EffectScope
  • 仅供内部使用

Pinia 实例的 effect scope。用于内部清理。

插件系统

插件上下文

创建插件时,你会收到一个包含以下属性的上下文对象:

ts
interface PiniaPluginContext {
  pinia: Pinia
  app: App
  store: Store
  options: DefineStoreOptions
}

属性

  • pinia: Pinia 实例
  • app: Vue 应用实例(仅 Vue 3)
  • store: 正在增强的 store
  • options: 传递给 defineStore() 的选项对象

插件示例

简单属性插件

js
function addSecretPlugin() {
  return { secret: 'the cake is a lie' }
}

pinia.use(addSecretPlugin)

路由器插件

js
import { markRaw } from 'vue'
import { router } from './router'

function routerPlugin({ store }) {
  store.router = markRaw(router)
}

pinia.use(routerPlugin)

持久化插件

js
import { watch } from 'vue'

function persistencePlugin({ store }) {
  const storageKey = `pinia-${store.$id}`
  
  // 从 localStorage 恢复
  const saved = localStorage.getItem(storageKey)
  if (saved) {
    store.$patch(JSON.parse(saved))
  }
  
  // 变化时保存到 localStorage
  watch(
    () => store.$state,
    (state) => {
      localStorage.setItem(storageKey, JSON.stringify(state))
    },
    { deep: true }
  )
}

pinia.use(persistencePlugin)

TypeScript

插件类型

ts
import type { PiniaPluginContext } from 'pinia'

function myPlugin(context: PiniaPluginContext) {
  // 插件实现
}

扩展 Store 属性

通过插件向 store 添加属性时,扩展 PiniaCustomProperties 接口:

ts
import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomProperties {
    router: Router
    secret: string
  }
}

扩展 Store 状态

对于状态属性,扩展 PiniaCustomStateProperties

ts
import 'pinia'

declare module 'pinia' {
  export interface PiniaCustomStateProperties<S> {
    lastUpdated: Date
  }
}

SSR 支持

服务端

js
// server.js
import { createPinia } from 'pinia'
import { createSSRApp } from 'vue'

export function createApp() {
  const app = createSSRApp({})
  const pinia = createPinia()
  
  app.use(pinia)
  
  return { app, pinia }
}

客户端水合

js
// client.js
import { createApp } from './main'

const { app, pinia } = createApp()

// 从服务器水合状态
if (window.__PINIA_STATE__) {
  pinia.state.value = window.__PINIA_STATE__
}

app.mount('#app')

测试

创建测试实例

js
import { createPinia, setActivePinia } from 'pinia'
import { beforeEach } from 'vitest'

beforeEach(() => {
  const pinia = createPinia()
  setActivePinia(pinia)
})

模拟插件

js
import { createPinia } from 'pinia'

const pinia = createPinia()

// 测试用模拟插件
pinia.use(() => ({
  $api: {
    get: vi.fn(),
    post: vi.fn()
  }
}))

最佳实践

插件顺序

js
const pinia = createPinia()

// 按依赖顺序添加插件
pinia.use(routerPlugin) // 基础功能
pinia.use(persistencePlugin) // 依赖状态
pinia.use(loggerPlugin) // 应该最后添加以完整记录

条件插件

js
const pinia = createPinia()

// 仅开发环境插件
if (process.env.NODE_ENV === 'development') {
  pinia.use(loggerPlugin)
  pinia.use(devtoolsPlugin)
}

// 仅生产环境插件
if (process.env.NODE_ENV === 'production') {
  pinia.use(analyticsPlugin)
}

插件配置

js
// 创建可配置插件
function createApiPlugin(baseURL) {
  return ({ store }) => {
    store.$api = createApiClient(baseURL)
  }
}

const pinia = createPinia()
pinia.use(createApiPlugin('https://api.example.com'))

生命周期

安装

  1. createPinia() 创建新实例
  2. app.use(pinia) 安装插件
  3. Pinia 设置全局属性并提供实例

Store 创建

  1. defineStore() 注册 store 定义
  2. 首次调用 useStore() 创建 store 实例
  3. 插件应用到新 store
  4. Store 添加到 Pinia 实例

清理

  1. 应用卸载时,Pinia 清理所有 store
  2. Effect scope 被销毁
  3. 订阅被移除

另请参阅

Released under the MIT License.