Skip to content
第 12 章 前端 ⏱ 13 分钟阅读

第 12 章:Pinia 进阶 ​

学习目标 ​

  • 用 pinia-plugin-persistedstate 持久化
  • 在路由守卫中读取 store 做权限
  • store 之间互相调用
  • 写单元测试

一、持久化插件 ​

bash
npm i pinia-plugin-persistedstate
ts
// src/main.ts
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
ts
// store 中开启
export const useUserStore = defineStore('user', {
  state: () => ({ token: '', name: '' }),
  persist: true,                      // 默认 localStorage
})

精确控制:

ts
persist: {
  key: 'myapp-user',
  storage: localStorage,
  paths: ['token'],                  // 只持久化 token
}

💡 persist 写在 store 配置对象的字段里,前提是装了 pinia-plugin-persistedstate 插件(不装插件,persist 字段被忽略,不生效)。

三步配置:

① 装插件

bash
npm i pinia-plugin-persistedstate

② main.ts 注册

ts
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPersist)   // ← 必须注册,不然 persist 字段不生效

③ 写在 store 里(Options 风格)

ts
// src/stores/user.ts
export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: { name: '', avatar: '' },
  }),

  persist: {
    key: 'myapp-user',                       // ① localStorage 里的 key 名
    storage: localStorage,                   // ② 默认就是 localStorage,可以不写
    paths: ['token'],                        // ③ 只持久化 token(不写 = 全 state 都存)
  },
})

⚠️ Setup 风格不能用 persist——语法不支持。要持久化就用 Options 风格,Setup 风格只用在组合式逻辑多时。

常用配置项:

字段干啥
keylocalStorage 里的 key 名(默认是 store id)
storage默认 localStorage,可换 sessionStorage
paths只持久化哪些字段,支持点路径(如 'settings.theme')
beforeRestore / afterRestore从 localStorage 读回前后钩子
serializer自定义序列化(默认 JSON)

啥时候用:

场景配 persist
token / 用户信息✅ 必须,刷新不掉登录
购物车 / 草稿✅ 用户体验好
全局 UI 偏好(主题 / 语言)✅ 推荐
纯计算状态 / 临时数据❌ 不持久化

⚠️ 坑 1:persist: true 默认会用 JSON.stringify,不要存函数 / Symbol / 循环引用。

二、store 之间互相调用 ​

ts
export const useOrderStore = defineStore('order', () => {
  const cart = useCartStore()
  const user = useUserStore()

  const checkout = async () => {
    if (!user.token) throw new Error('未登录')
    const res = await api.createOrder(cart.items)
    cart.clear()
    return res
  }

  return { checkout }
})

三、路由守卫中使用 store ​

ts
// src/router/index.ts
import { useUserStore } from '@/stores/user'

router.beforeEach((to) => {
  const user = useUserStore()
  if (to.meta.requiresAuth && !user.token) {
    return { path: '/login' }
  }
})

⚠️ 坑 2:在 router.beforeEach 里调 useStore 必须 Pinia 已经 app.use(pinia),否则会报错。

四、getter 互相引用 ​

ts
getters: {
  fullName: (s) => `${s.first} ${s.last}`,
  greeting(): string {
    return this.fullName + ' 你好'    // ⚠️ 必须用 this,不能用 state.
  },
}

五、单元测试 ​

bash
npm i -D vitest @vue/test-utils
ts
// src/stores/__tests__/cart.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCartStore } from '../cart'
import { beforeEach, expect, test } from 'vitest'

beforeEach(() => setActivePinia(createPinia()))

test('add item', () => {
  const cart = useCartStore()
  cart.add({ id: 1, name: 'A' })
  cart.add({ id: 1, name: 'A' })
  expect(cart.totalQty).toBe(2)
})

⚠️ 坑 3:每个测试前 setActivePinia(createPinia()),否则 store 状态会串。

六、热更新 ​

ts
// vite HMR 支持
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
}

这样 store 改动后,页面状态不会丢。

七、组合式风格 vs 选项式 ​

风格优点适用
Setup灵活,可复用 useXxx复杂业务
Options直观,IDE 提示好简单业务

⚠️ 坑 4:同一项目统一风格,混用维护成本高。

八、本章小结 ​

能力关键
持久化persist: true + 插件
跨 store在 action 中 useXStore()
测试setActivePinia(createPinia())
HMRacceptHMRUpdate(useStore, import.meta.hot)

动手练习 ​

  1. 持久化:token 用 persist 持久化,刷新页面后仍登录
  2. 订单 store:加 OrderStore,在 checkout 中调用 cart + user
  3. 单元测试:给 cart store 写 3 个 case

下一章:第 13 章:Composition API 实战 →

本站基于 VitePress 构建 · 由 StackHub 团队维护