14 changed files with 295 additions and 214 deletions
@ -1,22 +1,32 @@ |
|||
import { getStorageShortName } from '/@/utils/env'; |
|||
import { createStorage as create } from './storageCache'; |
|||
import { createStorage as create, CreateStorageParams } from './storageCache'; |
|||
import { enableStorageEncryption } from '/@/settings/encryptionSetting'; |
|||
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; |
|||
|
|||
const createOptions = (storage = sessionStorage) => { |
|||
export type Options = Partial<CreateStorageParams>; |
|||
|
|||
const createOptions = (storage: Storage, options: Options = {}): Options => { |
|||
return { |
|||
// No encryption in debug mode
|
|||
hasEncrypt: enableStorageEncryption, |
|||
storage, |
|||
prefixKey: getStorageShortName(), |
|||
timeout: DEFAULT_CACHE_TIME, |
|||
|
|||
...options, |
|||
}; |
|||
}; |
|||
|
|||
export const WebStorage = create(createOptions()); |
|||
export const WebStorage = create(createOptions(sessionStorage)); |
|||
|
|||
export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => { |
|||
return create(createOptions(storage, options)); |
|||
}; |
|||
|
|||
export const createStorage = (storage: Storage = sessionStorage) => { |
|||
return create(createOptions(storage))!; |
|||
export const createPersistentStorage = ( |
|||
storage: Storage = sessionStorage, |
|||
options: Options = {} |
|||
) => { |
|||
return createStorage(storage, { ...options, timeout: DEFAULT_CACHE_TIME }); |
|||
}; |
|||
|
|||
export default WebStorage; |
|||
|
|||
@ -0,0 +1,98 @@ |
|||
export interface Cache<V = any> { |
|||
value?: V; |
|||
timeoutId?: ReturnType<typeof setTimeout>; |
|||
time?: number; |
|||
alive?: number; |
|||
} |
|||
|
|||
const NOT_ALIVE = 0; |
|||
|
|||
export class Memory<T = any, V = any> { |
|||
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]; |
|||
} |
|||
|
|||
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) { |
|||
if (item.timeoutId) { |
|||
clearTimeout(item.timeoutId); |
|||
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 * 1000; |
|||
item.timeoutId = setTimeout(() => { |
|||
this.remove(key); |
|||
}, expires); |
|||
|
|||
return value; |
|||
} |
|||
|
|||
remove<K extends keyof T>(key: K) { |
|||
const item = this.get(key); |
|||
Reflect.deleteProperty(this.cache, 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 = now + item.time * 1000; |
|||
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 = {}; |
|||
} |
|||
} |
|||
@ -1,130 +1,109 @@ |
|||
import { createStorage } from '/@/utils/cache'; |
|||
|
|||
import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; |
|||
|
|||
const ls = createStorage(localStorage); |
|||
const ss = createStorage(); |
|||
|
|||
interface CacheStore { |
|||
local: Recordable; |
|||
session: Recordable; |
|||
import { createPersistentStorage } 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]: Recordable; |
|||
[ROLES_KEY]: Recordable; |
|||
[LOCK_INFO_KEY]: Recordable; |
|||
[PROJ_CFG_KEY]: Recordable; |
|||
} |
|||
|
|||
/** |
|||
* @description: Persistent cache |
|||
*/ |
|||
const cacheStore: CacheStore = { |
|||
// localstorage cache
|
|||
local: {}, |
|||
// sessionstorage cache
|
|||
session: {}, |
|||
}; |
|||
|
|||
function initCache() { |
|||
cacheStore.local = ls.get(BASE_LOCAL_CACHE_KEY) || {}; |
|||
cacheStore.session = ss.get(BASE_SESSION_CACHE_KEY) || {}; |
|||
} |
|||
type LocalStore = BasicStore; |
|||
|
|||
initCache(); |
|||
type SessionStore = BasicStore; |
|||
|
|||
export function setLocal(key: string, value: any, immediate = false) { |
|||
const local = ls.get(BASE_LOCAL_CACHE_KEY)?.[BASE_LOCAL_CACHE_KEY] || {}; |
|||
export type BasicKeys = keyof BasicStore; |
|||
type LocalKeys = keyof LocalStore; |
|||
type SessionKeys = keyof SessionStore; |
|||
|
|||
cacheStore.local[BASE_LOCAL_CACHE_KEY] = |
|||
{ ...local, ...cacheStore.local[BASE_LOCAL_CACHE_KEY] } || {}; |
|||
cacheStore.local[BASE_LOCAL_CACHE_KEY][key] = value; |
|||
const ls = createPersistentStorage(localStorage); |
|||
const ss = createPersistentStorage(sessionStorage); |
|||
|
|||
if (immediate) { |
|||
ls.set(BASE_LOCAL_CACHE_KEY, cacheStore.local); |
|||
} |
|||
} |
|||
const localMemory = new Memory(DEFAULT_CACHE_TIME); |
|||
const sessionMemory = new Memory(DEFAULT_CACHE_TIME); |
|||
|
|||
export function getLocal<T>(key: string): T | null { |
|||
try { |
|||
return cacheStore.local[BASE_LOCAL_CACHE_KEY][key]; |
|||
} catch (error) { |
|||
return null; |
|||
} |
|||
function initMemory() { |
|||
const localCache = ls.get(APP_LOCAL_CACHE_KEY); |
|||
const sessionCache = ls.get(APP_SESSION_CACHE_KEY); |
|||
localCache && localMemory.resetCache(localCache); |
|||
sessionCache && sessionMemory.resetCache(sessionCache); |
|||
} |
|||
|
|||
export function removeLocal(key: string) { |
|||
if (cacheStore.local[BASE_LOCAL_CACHE_KEY]) { |
|||
Reflect.deleteProperty(cacheStore.local[BASE_LOCAL_CACHE_KEY], key); |
|||
initMemory(); |
|||
export class Persistent { |
|||
static getLocal<T>(key: LocalKeys) { |
|||
return localMemory.get(key)?.value as Nullable<T>; |
|||
} |
|||
} |
|||
|
|||
export function clearLocal(immediate = false) { |
|||
cacheStore.local = {}; |
|||
immediate && ls.remove(BASE_LOCAL_CACHE_KEY); |
|||
} |
|||
|
|||
export function setSession(key: string, value: any, immediate = false) { |
|||
const session = ss.get(BASE_SESSION_CACHE_KEY)?.[BASE_SESSION_CACHE_KEY] || {}; |
|||
|
|||
cacheStore.session[BASE_SESSION_CACHE_KEY] = |
|||
{ ...session, ...cacheStore.session[BASE_SESSION_CACHE_KEY] } || {}; |
|||
static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void { |
|||
localMemory.set(key, toRaw(value)); |
|||
immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache); |
|||
} |
|||
|
|||
cacheStore.session[BASE_SESSION_CACHE_KEY][key] = value; |
|||
static removeLocal(key: LocalKeys): void { |
|||
localMemory.remove(key); |
|||
} |
|||
|
|||
if (immediate) { |
|||
ss.set(BASE_SESSION_CACHE_KEY, cacheStore.session); |
|||
static clearLocal(): void { |
|||
localMemory.clear(); |
|||
} |
|||
} |
|||
|
|||
export function removeSession(key: string) { |
|||
if (cacheStore.session[BASE_SESSION_CACHE_KEY]) { |
|||
Reflect.deleteProperty(cacheStore.session[BASE_SESSION_CACHE_KEY], key); |
|||
static getSession<T>(key: SessionKeys) { |
|||
return sessionMemory.get(key)?.value as Nullable<T>; |
|||
} |
|||
} |
|||
|
|||
export function getSession<T>(key: string): T | null { |
|||
try { |
|||
return cacheStore.session[BASE_SESSION_CACHE_KEY][key]; |
|||
} catch (error) { |
|||
return null; |
|||
static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void { |
|||
sessionMemory.set(key, toRaw(value)); |
|||
immediate && ss.set(APP_SESSION_CACHE_KEY, localMemory); |
|||
} |
|||
} |
|||
|
|||
export function clearSession(immediate = false) { |
|||
cacheStore.session = {}; |
|||
immediate && ss.remove(BASE_SESSION_CACHE_KEY); |
|||
} |
|||
static removeSession(key: SessionKeys): void { |
|||
sessionMemory.remove(key); |
|||
} |
|||
static clearSession(): void { |
|||
sessionMemory.clear(); |
|||
} |
|||
|
|||
export function clearAll() { |
|||
clearLocal(); |
|||
clearSession(); |
|||
static clearAll() { |
|||
sessionMemory.clear(); |
|||
localMemory.clear(); |
|||
} |
|||
} |
|||
|
|||
export function persistentCache() { |
|||
const localCache = cacheStore.local; |
|||
const sessionCache = cacheStore.session; |
|||
ls.set(BASE_LOCAL_CACHE_KEY, localCache); |
|||
ss.set(BASE_SESSION_CACHE_KEY, sessionCache); |
|||
} |
|||
window.addEventListener('beforeunload', function () { |
|||
ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache); |
|||
ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache); |
|||
}); |
|||
|
|||
(() => { |
|||
// /** Write to local before closing window */
|
|||
window.addEventListener('beforeunload', () => { |
|||
persistentCache(); |
|||
}); |
|||
function storageChange(e: any) { |
|||
const { key, newValue, oldValue } = e; |
|||
|
|||
function storageChange(e: any) { |
|||
const { key, newValue, oldValue } = e; |
|||
if (!key) { |
|||
Persistent.clearAll(); |
|||
return; |
|||
} |
|||
|
|||
if (!key) { |
|||
clearAll(); |
|||
return; |
|||
if (!!newValue && !!oldValue) { |
|||
if (APP_LOCAL_CACHE_KEY === key) { |
|||
Persistent.clearLocal(); |
|||
} |
|||
|
|||
if (!!newValue && !!oldValue) { |
|||
if (BASE_LOCAL_CACHE_KEY === key) { |
|||
clearLocal(); |
|||
} |
|||
if (BASE_SESSION_CACHE_KEY === key) { |
|||
clearSession(); |
|||
} |
|||
if (APP_SESSION_CACHE_KEY === key) { |
|||
Persistent.clearSession(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
window.addEventListener('storage', storageChange); |
|||
|
|||
window.addEventListener('storage', storageChange); |
|||
})(); |
|||
export default {}; |
|||
|
|||
Loading…
Reference in new issue