Pinia持久化

pinia-plugin-persist插件实现Pinia的数据持久化

食用方法

安装
# npm
npm install pinia-plugin-persist

# yarn
yarn add pinia-plugin-persist

# pnpm
pnpm add pinia-plugin-persist

store/index.ts配置

import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'

const pinia = createPinia()
pinia.use(piniaPersist)

tsconfig.ts添加配置

// tsconfig.ts
{
"compilerOptions": {
"types": [
"pinia-plugin-persist"
]
},
}
Usage

源码即文档

export interface PersistStrategy {
key?: string;
storage?: Storage;
paths?: string[];
}
export interface PersistOptions {
enabled: true;
strategies?: PersistStrategy[];
}
declare type Store = PiniaPluginContext['store'];
declare module 'pinia' {
interface DefineStoreOptionsBase<S, Store> {
persist?: PersistOptions;
}
}

1. 基本使用

import { defineStore } from "pinia"

export const useStore = defineStore("StoreId", {
state: () => ({
foo: "foo",
bar: "bar"
}),
// 开启数据缓存
persist: {
enabled: true,
strategies: [
{
key: 'user', //自定义 Key值
storage: localStorage, // 选择存储方式
path: ['foo'], // 默认所有 state 都会进行缓存,通过 paths 指定要持久化的字段,其余的则不会进行持久化
},
]
}
})

开启 enabled 之后,默认会对整个 Storestate 内容进行 sessionStorage 储存。

2. 进阶用法

strategies 字段说明:

属性 描述
key 自定义存储的 key,默认是 store.$id
storage 可以指定任何 extends Storage 的实例,默认是 sessionStorage
paths state 中的字段名,按组打包储存(默认所有 state 都会进行缓存,通过 paths 指定要持久化的字段,其余的则不会进行持久化)
import { defineStore } from "pinia"

export const useStore = defineStore("YourStore", () => {
const foo = ref("foo")
const bar = ref("bar")
return { foo, bar }
}, {
enabled: true,
strategies: [{
// 自定义存储的 key,默认是 store.$id
key: "custom storageKey",
// 可以指定任何 extends Storage 的实例,默认是 sessionStorage
storage: localStorage,
// state 中的字段名,按组打包储存
paths: ["foo", "bar"]
}]
})

storage 属性可以使用任何继承自 Storage 协议的对象,自定义存储对象也可以,如下 cookiesStorage 为例

import Cookies from 'js-cookie'

const cookiesStorage: Storage = {
setItem (key, state) {
return Cookies.set('accessToken', state.accessToken, { expires: 3 })
},
getItem (key) {
return JSON.stringify({
accessToken: Cookies.getJSON('accessToken'),
})
},
}

export const useStore = defineStore("YourStore", () => {
const foo = ref("foo")
const bar = ref("bar")
const accessToken = ref("xxx")
return { foo, bar, accessToken }
}, {
enabled: true,
strategies: [{
key: "token",
storage: cookiesStorage,
paths: ["accessToken"]
}]
})

源码解读

type Store = PiniaPluginContext['store'];
type PartialState = Partial<Store['$state']>;

export const updateStorage = (strategy: PersistStrategy, store: Store) => {

// 默认使用 sessionStorage
const storage = strategy.storage || sessionStorage

// 默认存储 key 为 store.$id
const storeKey = strategy.key || store.$id

if (strategy.paths) {

// 遍历 paths 将对应的属性收集到 finalObj 中
const partialState = strategy.paths.reduce((finalObj, key) => {
finalObj[key] = store.$state[key]
return finalObj
}, {} as PartialState)

// 执行存储
storage.setItem(storeKey, JSON.stringify(partialState))
} else {

// 如果没有 paths,则按整个 store.$state 存储
storage.setItem(storeKey, JSON.stringify(store.$state))
}
}


export default ({ options, store }: PiniaPluginContext): void => {

// 判断插件功能是否开启
if (options.persist?.enabled) {

// 默认策略实例
const defaultStrat: PersistStrategy[] = [{
key: store.$id,
storage: sessionStorage,
}]

const strategies = options.persist?.strategies?.length ? options.persist?.strategies : defaultStrat

strategies.forEach((strategy) => {
const storage = strategy.storage || sessionStorage
const storeKey = strategy.key || store.$id
const storageResult = storage.getItem(storeKey)

if (storageResult) {

// 如果 storage 中存在同步数据
store.$patch(JSON.parse(storageResult))
updateStorage(strategy, store)
}
})

store.$subscribe(() => {

// 监听 state 变化,同步更新 storage
strategies.forEach((strategy) => {
updateStorage(strategy, store)
})
})
}
}