Chapter 07

插件与持久化

Pinia 的可扩展性——写自定义插件为所有 store 注入能力、用官方持久化插件把状态存进 localStorage,并监听变化。

7.1 核心概念

Pinia 插件
一个接收 context 的函数,通过 pinia.use(plugin) 注册,对每个 store 生效一次。
context
插件参数,含 storeoptionspiniaapp,可读取 store 定义并扩展它。
$subscribe
订阅某个 store 的 state 变化,回调收到 mutation 描述与最新 state。
$onAction
拦截 action 调用,可在 before/after/onError 三个时机插入逻辑(如日志、埋点)。
持久化(Persistence)
把 store state 写入 localStorage/sessionStorage,刷新页面后自动恢复。
persistedstate 插件
社区事实标准 pinia-plugin-persistedstate,一行配置实现持久化。

7.2 编写一个插件

插件本质是往每个 store 上"挂东西"。最简单的插件给所有 store 加一个只读属性:

// plugins/hello.ts
import { PiniaPluginContext } from "pinia";

export function helloPlugin({ store }: PiniaPluginContext) {
  // 给每个 store 注入 createdAt
  store.createdAt = new Date();

  // 返回的对象会合并进 store(会被 DevTools 追踪)
  return { secret: "the cake is a lie" };
}
// main.ts 注册
const pinia = createPinia();
pinia.use(helloPlugin);
app.use(pinia);
插件能做什么

注入全局属性(如 router、i18n)、给所有 store 添加公共 action、包裹 action 记日志、实现持久化——凡是"对每个 store 都想做的事"都适合写成插件。

7.3 给 TypeScript 声明插件注入的属性

// 扩展类型,让 store.createdAt 有类型提示
import "pinia";

declare module "pinia" {
  export interface PiniaCustomProperties {
    createdAt: Date;
    secret: string;
  }
}

7.4 使用官方持久化插件

$ npm i pinia-plugin-persistedstate
// main.ts
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);

然后在需要持久化的 store 上开启 persist

export const useAuthStore = defineStore("auth", {
  state: () => ({ token: "", user: null as User | null }),
  actions: { /* ... */ },
  // 最简:整个 state 存入 localStorage,key 用 store id
  persist: true,
});

精细化配置

persist: {
  key: "my-auth",                 // 自定义存储 key
  storage: sessionStorage,        // 默认 localStorage
  pick: ["token"],                 // 只持久化部分字段
},
选项作用默认
key存储键名store id
storagelocalStorage / sessionStoragelocalStorage
pick只持久化指定字段全部 state
omit排除指定字段
serializer自定义序列化(如加密)JSON
别持久化敏感/大体积数据

token 等敏感信息存 localStorage 有 XSS 风险,考虑 httpOnly Cookie;大对象会拖慢每次写入。用 pick 精确控制持久化范围。

7.5 $subscribe 订阅 state 变化

const cart = useCartStore();

cart.$subscribe((mutation, state) => {
  // mutation.type: 'direct' | 'patch object' | 'patch function'
  console.log(mutation.type, mutation.storeId);
  // 手动持久化示例
  localStorage.setItem("cart", JSON.stringify(state));
}, { detached: false });

7.6 $onAction 拦截 action

cart.$onAction(({ name, args, after, onError }) => {
  const start = Date.now();
  console.log(`调用 action: ${name}`, args);

  after((result) => {
    console.log(`${name} 完成,耗时 ${Date.now() - start}ms`);
  });

  onError((err) => {
    console.error(`${name} 出错`, err);
  });
});
$onAction 用途

统一日志、性能监控、埋点上报、错误上报(Sentry)——不用改动每个 action,在插件里对所有 store 注册一次即可全局生效。

7.7 小结与下一步