数据持久化
实现原理
数据持久化存储方案采用 vuex + localStorage,先将数据存储到 vuex,当页面刷新或者离开会触发对应的钩子 同步到 localStorage,实现数据持久化。
当重新访问页面时,从 localStorage 读取数据,实现数据恢复。在这个过程中会进行数据校验,保证数据的完整性。
vuex
vuex 模块介绍
bash
├── store
│ ├── modules
│ └── menu.ts # 菜单
│ └── setting.ts # 设置中心
│ └── user.ts # 用户
│ └── worktab.ts # 多标签页
menu
- menuList // 菜单列表
- menuWidth // 菜单宽度
setting
- systemThemeType // 全局主题类型 light dark
- systemThemeMode // 全局主题模式 light dark auto
- menuThemeType // 菜单主题类型
- systemThemeColor // 系统主题颜色
- boxBorderMode // 盒子模式 border | shadow
- uniqueOpened // 是否开启手风琴模式
- showMenuButton // 是否显示菜单展开按钮
- showRefreshButton // 是否显示页面刷新按钮
- showCrumbs // 是否显示全局面包屑
- autoClose // 设置后是否自动关闭窗口
- showWorkTab // 是否显示多标签
- showLanguage // 是否显示多语言选择
- showNprogress // 是否显示顶部进度条
- colorWeak // 是否显示顶部进度条
- showSettingGuide // 是否显示设置引导
- pageTransition // 页面切换动画
- menuOpen // 菜单是否展开
- refresh
user
- language // 语言
- isLogin // 是否登录
- info // 用户信息
- searchHistory // 搜索历史
worktab
- current // 当前标签页
- opened // 打开的标签页
localStorage
使用版本号 + 数据校验进本地数据管理,当数据不正确的时候,会清空本地数据,并跳转至登录页面,保证用户正常访问。
如何保证数据完整性?
1、版本号管理
使用版本号管理 localStorage 数据,并将新数据合并到现有数据中
ts
// 数据持久化存储
function saveStoreStorage<T>(newData: T) {
const version = import.meta.env.VITE_VERSION;
initVersion(version);
const vs = localStorage.getItem("version") || version;
const storedData = JSON.parse(localStorage.getItem(`sys-v${vs}`) || "{}");
// 合并新数据与现有数据
const mergedData = { ...storedData, ...newData };
localStorage.setItem(`sys-v${vs}`, JSON.stringify(mergedData));
}
2、数据校验
在 /src/utils/storage.ts 文件中封装了数据校验方法,在页面刷新时,会调用该方法,判断数据是否完整,不正确则清空本地存储,并跳转至登录页面。
ts
// 验证本地存储数据并处理异常
export function validateStorageData() {
if (location.href.includes("/login")) return true;
const schema = {
user: {
info: "object",
isLogin: "boolean",
language: "string",
worktab: {
current: {
title: "string",
title_en: "string",
path: "string",
params: "object",
query: "object",
},
opened: "object",
},
setting: {
systemThemeType: "string",
systemThemeMode: "string",
menuThemeType: "string",
boxBorderMode: "boolean",
uniqueOpened: "boolean",
systemThemeColor: "string",
showMenuButton: "boolean",
showRefreshButton: "boolean",
showCrumbs: "boolean",
autoClose: "boolean",
showWorkTab: "boolean",
showLanguage: "boolean",
showNprogress: "boolean",
colorWeak: "boolean",
showSettingGuide: "boolean",
refresh: "boolean",
},
},
};
try {
const data = JSON.parse(getSysStorage() || "{}");
// 模拟本地数据类型错误
// data.user.language = 2024
if (Object.keys(data).length === 0) {
logOut();
return false;
}
if (!validate(data, schema)) {
throw new Error("本地存储数据结构异常");
}
return true;
} catch {
handleError();
return false;
}
}
// 将 vuex 中的数据保存到 localStorage 中(在即将离开页面(刷新或关闭)时执行)
export function saveUserData() {
const isiOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
const eventType = isiOS ? "pagehide" : "beforeunload";
window.addEventListener(eventType, () => {
useUserStore().saveUserData();
});
}