1.说明
本章解析VbenAdmin
中存储浏览器数据的四个地方:LocalStorage
、SessionStorage
、LocalMemory
、SessionMemory
。
src\settings\projectSetting-permissionCacheType
只决定你的认证信息存储在LocalStorage
中还是SessionStorage
中。
- 你可以调用
src\utils\cache\persistent.ts
中的Persistent
的静态方法操作这些存储。
LocalStorage
中的COMMON__LOCAL__KEY__
存储着LocalMemory
的所有对象。当页面刷新的时候,LocalMemory
会获取LocalStorage
中的COMMON__LOCAL__KEY__
对应的值作为内存。
- 每当你向
LocalMeory
设置一个新值的时候,就会同步写到LocalStorage
中。
- 同理
SessionStorage
和SessionMemory
的关系也是如此。
- 为什么这么做,我觉得应该是
LocalStorage
和SessionStorage
中的数据都是生产环境加密的,这样可以减少运算吧。
2.index.ts
这个文件提供了一些方便我们创建 (操作Storage
的类) 的方法。
src\utils\cache\index.ts
import { getStorageShortName } from '/@/utils/env';
import { createStorage as create, CreateStorageParams } from './storageCache';
import { enableStorageEncryption } from '/@/settings/encryptionSetting';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
// Partial将类型变成可选的
export type Options = Partial<CreateStorageParams>;
const createOptions = (storage: Storage, options: Options = {}): Options => {
return {
// No encryption in debug mode
hasEncrypt: enableStorageEncryption,
storage,
prefixKey: getStorageShortName(),
...options,
};
};
export const WebStorage = create(createOptions(sessionStorage));
export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => {
return create(createOptions(storage, options));
};
export const createSessionStorage = (options: Options = {}) => {
return createStorage(sessionStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export const createLocalStorage = (options: Options = {}) => {
return createStorage(localStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export default WebStorage;
3.memory.ts
这个类定义Memory
的数据结构和内部的操作方法。
src\utils\cache\memory.ts
// 缓存接口
export interface Cache<V = any> {
// 值
value?: V;
// 计时器的ID
timeoutId?: ReturnType<typeof setTimeout>;
// 过期时间毫秒值(new Date + alive)
time?: number;
// 存活时间
alive?: number;
}
// 不存活为0
const NOT_ALIVE = 0;
// 内存类
export class Memory<T = any, V = any> {
// 缓存对象,一个Key一个缓存接口
private cache: { [key in keyof T]?: Cache<V> } = {};
// 默认存活时间
private alive: number;
constructor(alive = NOT_ALIVE) {
// Unit second
this.alive = alive * 1000;
}
// 返回这个缓存
get getCache() {
return this.cache;
}
// 设置缓存
setCache(cache) {
this.cache = cache;
}
// get<K extends keyof T>(key: K) {
// const item = this.getItem(key);
// const time = item?.time;
// if (!isNullOrUnDef(time) && time < new Date().getTime()) {
// this.remove(key);
// }
// return item?.value ?? undefined;
// }
get<K extends keyof T>(key: K) {
return this.cache[key];
}
// key、值、过期时间
set<K extends keyof T>(key: K, value: V, expires?: number) {
// 先获取
let item = this.get(key);
// 处理非法过期时间
if (!expires || (expires as number) <= 0) {
expires = this.alive;
}
// 如果原来缓存有值
if (item) {
// 如果计时器ID存在
if (item.timeoutId) {
// 清除计时器
clearTimeout(item.timeoutId);
// 计时器ID置空
item.timeoutId = undefined;
}
// 缓存赋值
item.value = value;
} else {
// 原值不存在,赋值
item = { value, alive: expires };
this.cache[key] = item;
}
// 如果过期时间不存在
if (!expires) {
// 返回值
return value;
}
item.time = new Date().getTime() + this.alive;
item.timeoutId = setTimeout(() => {
this.remove(key);
}, expires);
return value;
}
remove<K extends keyof T>(key: K) {
const item = this.get(key);
// 缓存对象上删除这个Key
Reflect.deleteProperty(this.cache, key);
// 如果缓存的Key对应的值存在
if (item) {
// 清除定时器
clearTimeout(item.timeoutId!);
return item.value;
}
}
// 将传入的缓存对象设置到缓存中
resetCache(cache: { [K in keyof T]: Cache }) {
Object.keys(cache).forEach((key) => {
const k = (key as any) as keyof T;
const item = cache[k];
if (item && item.time) {
const now = new Date().getTime();
const expire = item.time;
if (expire > now) {
this.set(k, item.value, expire);
}
}
});
}
// 清除内存
clear() {
// 先将所有计时器去除
Object.keys(this.cache).forEach((key) => {
const item = this.cache[key];
item.timeoutId && clearTimeout(item.timeoutId);
});
// 将缓存清空
this.cache = {};
}
}
4.persistent.ts
这个文件提供了外界操作Storage
和Memory
的方法。
src\utils\cache\persistent.ts
// import type { LockInfo, UserInfo } from '/@/store/types';
import { ProjectConfig } from '/#/config';
import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
import { Memory } from './memory';
import {
TOKEN_KEY,
USER_INFO_KEY,
ROLES_KEY,
LOCK_INFO_KEY,
PROJ_CFG_KEY,
APP_LOCAL_CACHE_KEY,
APP_SESSION_CACHE_KEY,
} from '/@/enums/cacheEnum';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
import { toRaw } from 'vue';
// 基础存储接口
interface BasicStore {
[TOKEN_KEY]: string | number | null | undefined;
// [USER_INFO_KEY]: UserInfo;
[USER_INFO_KEY]: any;
[ROLES_KEY]: string[];
// [LOCK_INFO_KEY]: LockInfo;
[LOCK_INFO_KEY]: any;
[PROJ_CFG_KEY]: ProjectConfig;
}
// LocalStore存储接口
type LocalStore = BasicStore;
// SessionStore存储接口
type SessionStore = BasicStore;
// 基础存储接口的Key
export type BasicKeys = keyof BasicStore;
// LocalStore存储接口Key
type LocalKeys = keyof LocalStore;
// SessionStore存储接口Key
type SessionKeys = keyof SessionStore;
// 创建本地存储
const ls = createLocalStorage();
// 创建Session存储
const ss = createSessionStorage();
// DEFAULT_CACHE_TIME为内存的默认存活时间
// 创建本地内存
const localMemory = new Memory(DEFAULT_CACHE_TIME);
// 创建Session内存
const sessionMemory = new Memory(DEFAULT_CACHE_TIME);
// 初始化持久化内存
function initPersistentMemory() {
// 获取LocalStorage中的COMMON__LOCAL__KEY__的值
const localCache = ls.get(APP_LOCAL_CACHE_KEY);
//获取SessionStorage中的COMMON__SESSION__KEY__的值
const sessionCache = ss.get(APP_SESSION_CACHE_KEY);
// 如果存了值,就在内存中也存一份
localCache && localMemory.resetCache(localCache);
sessionCache && sessionMemory.resetCache(sessionCache);
}
// 导出一个持久化类,这里面的方法都是静态的
export class Persistent {
// 获取LocalStorage的内存
static getLocal<T>(key: LocalKeys) {
return localMemory.get(key)?.value as Nullable<T>;
}
// 设置LocalStorage的内存
static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void {
localMemory.set(key, toRaw(value));
immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
}
// 移出LocalStorage的内存
static removeLocal(key: LocalKeys): void {
localMemory.remove(key);
}
// 清除LocalStorage的内存
static clearLocal(): void {
localMemory.clear();
}
// 获取SessionStorage的内存
static getSession<T>(key: SessionKeys) {
return sessionMemory.get(key)?.value as Nullable<T>;
}
// 设置SessionStorage的内存
static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void {
sessionMemory.set(key, toRaw(value));
immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory);
}
// 移出SessionStorage的内存
static removeSession(key: SessionKeys): void {
sessionMemory.remove(key);
}
// 清除SessionStorage的内存
static clearSession(): void {
sessionMemory.clear();
}
// 将两种内存都清除
static clearAll() {
sessionMemory.clear();
localMemory.clear();
}
}
// 当窗口刷新或者关闭窗口
window.addEventListener('beforeunload', function () {
// 将内存加载到LocalStorage
ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
// 将内存加载到SessionStorage
ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache);
});
// 当存储库修改时
function storageChange(e: any) {
const { key, newValue, oldValue } = e;
if (!key) {
Persistent.clearAll();
return;
}
if (!!newValue && !!oldValue) {
if (APP_LOCAL_CACHE_KEY === key) {
Persistent.clearLocal();
}
if (APP_SESSION_CACHE_KEY === key) {
Persistent.clearSession();
}
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
// https://zhuanlan.zhihu.com/p/70497745
// 只监听LocalStorage的变化
// 监听其他窗口是否改变了LocalStorage,当前窗口修改的不处理
window.addEventListener('storage', storageChange);
// 将LocalStorage和SessionStorage加载到内存中
initPersistentMemory();
// console.log('初始化localMemory', localMemory);
// console.log('初始化sessionMemory', sessionMemory);
5.storageCache.ts
这个类封装了对Storage
的操作。
src\utils\cache\storageCache.ts
import { cacheCipher } from '/@/settings/encryptionSetting';
import type { EncryptionParams } from '/@/utils/cipher';
import { AesEncryption } from '/@/utils/cipher';
import { isNullOrUnDef } from '/@/utils/is';
// 创建缓存的参数
export interface CreateStorageParams extends EncryptionParams {
prefixKey: string;
storage: Storage;
hasEncrypt: boolean;
timeout?: Nullable<number>;
}
// 创建一个存储库
// Partial将类型的所有属性变成可选的
export const createStorage = ({
prefixKey = '',
storage = sessionStorage,
key = cacheCipher.key,
iv = cacheCipher.iv,
timeout = null,
hasEncrypt = true,
}: Partial<CreateStorageParams> = {}) => {
// 如果需要加密,加密key和偏移量需要为16bit
if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
}
// 使用Aes对称加密
const encryption = new AesEncryption({ key, iv });
/**
*Cache class
*Construction parameters can be passed into sessionStorage, localStorage,
* @class Cache
* @example
*/
const WebStorage = class WebStorage {
// 存储库
private storage: Storage;
// 前缀Key
private prefixKey?: string;
// 加密器
private encryption: AesEncryption;
// 是否需要加密
private hasEncrypt: boolean;
/**
*
* @param {*} storage
*/
constructor() {
this.storage = storage;
this.prefixKey = prefixKey;
this.encryption = encryption;
this.hasEncrypt = hasEncrypt;
}
private getKey(key: string) {
// 前缀 + 参数变大写
return `${this.prefixKey}${key}`.toUpperCase();
}
/**
*
* Set cache
* @param {string} key
* @param {*} value
* @expire Expiration time in seconds
* @memberof Cache
*/
// 设置一个值
set(key: string, value: any, expire: number | null = timeout) {
const stringData = JSON.stringify({
value,
time: Date.now(),
expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
});
// 最终存储的字符串,判断需不需要加密
const stringifyValue = this.hasEncrypt
? this.encryption.encryptByAES(stringData)
: stringData;
// storage设置Key,Value
this.storage.setItem(this.getKey(key), stringifyValue);
}
/**
*Read cache
* @param {string} key
* @memberof Cache
*/
get(key: string, def: any = null): any {
const val = this.storage.getItem(this.getKey(key));
if (!val) return def;
try {
// 如果有加密,就解密
const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
// 获取解密对象
const data = JSON.parse(decVal);
// 查看是否过期
const { value, expire } = data;
if (isNullOrUnDef(expire) || expire >= new Date().getTime()) {
return value;
}
// 如果过期,删除Key,但是这里没有返回了。
this.remove(key);
} catch (e) {
return def;
}
}
/**
* Delete cache based on key
* @param {string} key
* @memberof Cache
*/
remove(key: string) {
this.storage.removeItem(this.getKey(key));
}
/**
* Delete all caches of this instance
* 删除所有缓存
*/
clear(): void {
this.storage.clear();
}
};
return new WebStorage();
};
上一章
第七十二章-VbenAdmin的utils-auth解析
下一章
第七十四章-VbenAdmin的utils-event解析