vue pinia store优化
🛠 1. Storage 失效检测(比如:防止无痕模式 localStorage 崩溃)
问题背景:
某些浏览器(尤其是 iOS Safari 无痕模式)下,localStorage
是存在但不可写的。直接写会抛异常导致页面崩掉!
解决方案:
在 helper 初始化时,检测 localStorage 是否可用,如果不可用就降级(比如直接不做持久化)。
🛠 2. 存储超时机制(比如:24小时后过期清除)
问题背景:
即使 tab 关闭,localStorage 可能残留。需要自动设置超时时间,过期后自己清掉!
解决方案:
保存数据时附带一个 timestamp
,每次取数据时检测,如果超时了,自动清除。
🚀 全新升级版:utils/tab-storage-helper.ts
import { nanoid } from 'nanoid';
// ========== 配置区 ==========
// 是否开启“每个 TAB 独立存储”
const ENABLE_TAB_ISOLATION = true;
// tabId 存储到 sessionStorage 的 key
const SESSION_TAB_ID_KEY = 'tab-storage-helper-tab-id';
// 本地存储过期时间(毫秒),例如 24小时 = 24 * 60 * 60 * 1000
const STORAGE_EXPIRE_MS = 24 * 60 * 60 * 1000;
// ========== 工具函数 ==========
/** 检测 localStorage 是否可用 */
function isStorageAvailable() {
try {
const testKey = '__storage_test__';
localStorage.setItem(testKey, '1');
localStorage.removeItem(testKey);
return true;
} catch (e) {
console.warn('[TabStorageHelper] localStorage 不可用', e);
return false;
}
}
/** 生成 tabId */
function generateTabId() {
return nanoid(10);
}
/** 获取当前 tabId */
function getTabId() {
let tabId = sessionStorage.getItem(SESSION_TAB_ID_KEY);
if (!tabId) {
tabId = generateTabId();
sessionStorage.setItem(SESSION_TAB_ID_KEY, tabId);
}
return tabId;
}
/** 计算完整的存储 key */
function getStorageKey(baseKey: string) {
if (ENABLE_TAB_ISOLATION) {
const tabId = getTabId();
return `${baseKey}-${tabId}`;
}
return baseKey;
}
/** 自动清除当前 tab 存储 */
function clearStorage(baseKey: string) {
try {
const fullKey = getStorageKey(baseKey);
localStorage.removeItem(fullKey);
} catch (e) {
console.warn('[TabStorageHelper] 清理 localStorage 失败', e);
}
}
/** devtools 日志 */
function devLog(message: string, ...args: any[]) {
if (import.meta.env.DEV) {
console.log(`%c[TabStorageHelper]%c ${message}`, 'color:#00c4b6;font-weight:bold;', 'color:inherit;', ...args);
}
}
// ========== 核心导出 ==========
/** 创建 persist 配置 */
export function createPersistConfig(baseKey: string) {
const isAvailable = isStorageAvailable();
const storageKey = getStorageKey(baseKey);
// 初始化:监听 tab 关闭清理
if (ENABLE_TAB_ISOLATION && isAvailable) {
window.addEventListener('beforeunload', () => {
clearStorage(baseKey);
});
}
if (import.meta.env.DEV && ENABLE_TAB_ISOLATION) {
devLog(`TAB 隔离已启用,当前 tabId: ${sessionStorage.getItem(SESSION_TAB_ID_KEY)}`);
}
if (!isAvailable) {
devLog('localStorage 不可用,将禁用 persist 功能');
return undefined;
}
return {
key: storageKey,
storage: {
getItem: (key: string) => {
try {
const raw = localStorage.getItem(key);
if (!raw) return null;
const data = JSON.parse(raw);
if (data.__expireAt && Date.now() > data.__expireAt) {
// 已过期
localStorage.removeItem(key);
devLog(`检测到存储已过期,已清理: ${key}`);
return null;
}
return JSON.stringify(data.value);
} catch (e) {
console.warn('[TabStorageHelper] 读取失败', e);
return null;
}
},
setItem: (key: string, value: string) => {
try {
const data = {
value: JSON.parse(value),
__expireAt: Date.now() + STORAGE_EXPIRE_MS,
};
localStorage.setItem(key, JSON.stringify(data));
} catch (e) {
console.warn('[TabStorageHelper] 写入失败', e);
}
},
removeItem: (key: string) => {
try {
localStorage.removeItem(key);
} catch (e) {
console.warn('[TabStorageHelper] 移除失败', e);
}
},
},
};
}
✨ 新版的亮点
能力 | 说明 |
---|---|
localStorage 检测 | 防止无痕模式崩溃 |
自动存活检测 | 超过24小时,数据失效自动清理 |
依然保留 | 每个tab独立副本、tab关闭清理、统一配置 |
异常保护 | localStorage 全程 try-catch 包裹,稳 |
兼容性强 | 任何浏览器都能优雅降级 |
🛠 用法依旧简单
比如 store/app-graph.ts
继续这么写就行:
import { defineStore } from 'pinia';
import { createPersistConfig } from '@/utils/tab-storage-helper';
export const useAppGraphStore = defineStore('app-graph', {
state: () => ({
shapes: [],
selectedShapeId: null,
canvasZoom: 1,
}),
persist: createPersistConfig('app-graph'), // 自带隔离+超时检测
});
如果 localStorage 不可用,persist
会自动禁用。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。