状态仓库持久化
Vuex 状态仓库持久化
Vuex 的插件本质上是一个函数,它接收一个 store 实例作为参数,这个函数会在 store 初始化时执行。
store/index.js
import { createStore } from "vuex";
import counter from "./counter";
import text from "./text";
import createPersistPlugin from "./plugins/persist";
const store = createStore({
modules: {
counter,
text,
},
plugins: [
createPersistPlugin({
key: "my-app-store",
storage: localStorage,
paths: ["user", "settings.theme"], // 指定要持久化的字段
}),
],
});
export default store;
store/persistPlugin.js
// store/plugins/persist.ts
import { Store } from "vuex";
interface PersistOptions {
key?: string; // 存储的 key
storage?: Storage; // localStorage 或 sessionStorage
paths?: string[]; // 需要持久化的 state 路径
}
export default function createPersistPlugin(options: PersistOptions = {}) {
const { key = "vuex", storage = window.localStorage, paths } = options;
return (store: Store<any>) => {
// 1. 初始化:从 storage 中恢复数据
const savedState = storage.getItem(key);
if (savedState) {
const parsed = JSON.parse(savedState);
store.replaceState(Object.assign({}, store.state, parsed));
}
// 2. 订阅 mutation,每次更新都持久化
store.subscribe((_mutation, state) => {
let toSave = state; // 要保存的数据
// 如果指定了 paths,就只保存这些路径,否则保存所有数据
if (paths && paths.length > 0) {
// 只保存指定路径的数据
toSave = paths.reduce((subState: any, path) => {
const segments = path.split(".");
let data = state;
for (const seg of segments) {
if (data) data = data[seg];
}
subState[path] = data;
return subState;
}, {});
}
// 持久化数据
storage.setItem(key, JSON.stringify(toSave));
});
// 2. 也可以在每次离开页面时,保存数据
// window.addEventListener("beforeunload", () => {
// let toSave: any = store.state;
// if (paths && paths.length > 0) {
// toSave = paths.reduce((subState: any, path) => {
// const segments = path.split(".");
// let data: any = store.state;
// for (const seg of segments) {
// if (data) data = data[seg];
// }
// subState[path] = data;
// return subState;
// }, {});
// }
// storage.setItem(key, JSON.stringify(toSave));
// });
};
}
Pinia 状态仓库持久化
Pinia 的插件本质上是一个函数,它接收一个 context 对象作为参数。但是由于 Pinia 的仓库是分离的,所以这个函数会执行多次。
src/main.js
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import { createPersistPlugin } from "./plugins/persist";
const app = createApp(App);
const pinia = createPinia();
pinia.use(createPersistPlugin({ key: "my-app", paths: ["user", "settings.theme"] }));
app.use(pinia);
app.mount("#app");
plugins/persist.js
import type { PiniaPluginContext } from "pinia";
interface PersistOptions {
key?: string;
storage?: Storage;
paths?: string[];
}
export function createPersistPlugin(options: PersistOptions = {}) {
const { key = "pinia", storage = window.localStorage, paths } = options;
return (context: PiniaPluginContext) => {
const { store } = context;
// 仓库的 key 是 仓库名-仓库 id
const storeKey = `${key}-${store.$id}`;
// 1. 初始化:恢复数据
const savedState = storage.getItem(storeKey);
if (savedState) {
store.$patch(JSON.parse(savedState));
}
// 2. 页面关闭时存储
window.addEventListener("beforeunload", () => {
let toSave: any = store.$state;
if (paths && paths.length > 0) {
toSave = paths.reduce((subState: any, path) => {
const segments = path.split(".");
let data: any = store.$state;
for (const seg of segments) {
if (data) data = data[seg];
}
subState[path] = data;
return subState;
}, {});
}
storage.setItem(storeKey, JSON.stringify(toSave));
});
};
}