storage-base.js
/* eslint-disable */
/*
* @Description: sessionCache,sessionstorage、localstorage数据的存取方法,只做简单处理以及数据返回,错误在数据层处理
* @version: 0.0.1
* @Author: zengzengli
* @Date: 2019-12-16 21:05:50
* @LastEditors: Please set LastEditors
* @LastEditTime: 2020-11-21 16:25:06
*/
class StorageBase {
constructor(isLocalStorage) {
// isLocalStorage: true: localStorage false: sessionLsotage
this.isLocalStorage = isLocalStorage;
if (this.isLocalStorage) {
this.storage = window.localStorage;
} else {
this.storage = window.sessionStorage;
}
// isStorageAble:storage是否可用,包括window.name模拟降级可用
this.isStorageAble = this._checkStorage(this.storage);
if (!this.isStorageAble && !this.isLocalStorage) {
// window.name模拟sessionStorage
this.storage = {
getItem: this._winGetItem.bind(this),
setItem: this._winSetItem.bind(this),
removeItem: this._winRemoveItem.bind(this),
clear: this._winClear.bind(this),
};
this.isStorageAble = true;
}
}
/**
* @description: 判断是否支持webStorage
* @param {localStorage|sessionStorage} storage
* @return: {Bool} 是否支持webstorage
*/
_checkStorage(storage) {
const radomStr = Math.random()
.toString(36)
.substr(-10); // 生成10位由数字和字母组成的随机字符串
const key = `STOARGE_TEST${radomStr}`;
let value;
try {
storage.setItem(key, 1);
value = storage.getItem(key);
storage.removeItem(key);
return value === '1';
} catch (e) {
if (e.code === 22) {
// 排除可能由storage超限导致的错误
return true;
}
return false;
}
}
_getWindow() {
try {
return JSON.parse(window.name);
} catch (e) {
return {};
}
}
_setWindow(value) {
window.name = JSON.stringify(value);
}
_winGetItem(key) {
const data = this._getWindow();
return data[key];
}
_winSetItem(key, value) {
const data = this._getWindow();
data[key] = value;
this._setWindow(data);
}
_winRemoveItem(key) {
const data = this._getWindow();
delete data[key];
this._setWindow(data);
}
_winClear() {
window.name = '';
}
/**
* @description: 获取storage数据
* @return: {String|null}
*/
getItem(key) {
if (this.isStorageAble) {
try {
return this.storage.getItem(key);
} catch (e) {
return null;
}
}
}
/**
* @description: set storage数据
* @return: {undefind|Error}
*/
setItem(key, value) {
if (this.isStorageAble) {
try {
return this.storage.setItem(key, value);
} catch (e) {
return e;
}
}
}
/**
* @description: 移除storage数据
* @return: {undefind}
*/
removeItem(key) {
if (this.isStorageAble) {
return this.storage.removeItem(key);
}
}
/**
* @description: 清空storage
*/
clear() {
if (this.isStorageAble) {
this.storage.clear();
}
}
}
const localStorageCache = new StorageBase(true);
const sessionStorageCache = new StorageBase(false);
export { StorageBase, localStorageCache, sessionStorageCache };
stroage.js
/* eslint-disable */
/*
* @Description: 单key Storage 数据管理,出口仅放出单例,防止单页面多实例(保证缓存准确性)
* @version: 0.0.1
* @Author: zengzengli
* @Date: 2019-12-17 11:13:09
* @LastEditors: Please set LastEditors
* @LastEditTime: 2020-11-21 16:29:04
*/
import { sessionStorageCache, localStorageCache } from './storage-base';
// 单例命名空间
const NAMESPACE_KEY = 'LCT_OUT_MANAGER';
// 缓存剔除策略 FIFO(先进先出) FILO(先进后出) CE(清除最近将过期的)
const SHAFT_TYPE = ['FIFO', 'FILO', 'CE'];
// 强制删除允许尝试次数
const MAX_TRY_DELETE = 100;
export default class Storage {
constructor(opts) {
this.isLocalStorage = opts.isLocalStorage;
this._namespaceKey = opts.namespaceKey || NAMESPACE_KEY;
this.shiftType = opts.shiftCache || SHAFT_TYPE[0]; // 预留
this._readNeedUpdate = false; // 标记读数据时是否需要同步缓存数据,针对localStorage
// 统计单次造成的强制清除次数,防止无限强制清除
this.forceClearCount = 0;
if (this.isLocalStorage) {
this.storage = localStorageCache;
// 增加storage跨窗口监听,解决localstorage多窗口缓存失效问题
window.addEventListener(
'storage',
(e) => {
this._storageEvent(e);
},
false
);
} else {
this.storage = sessionStorageCache;
}
// 缓存是否可用
this.isStorageAble = this.storage.isStorageAble;
// 初始化缓存数据,读操作统一走缓存
this._getData();
}
/**
* @description: 增加storage跨窗口监听处理事件,解决localstorage多窗口缓存失效问题
*/
_storageEvent(e) {
if (e.key === NAMESPACE_KEY) {
this._readNeedUpdate = true;
}
}
/**
* @description: 获取storage最新数据
*/
_getData() {
try {
this.data = JSON.parse(this.storage.getItem(this._namespaceKey)) || {};
} catch (e) {
this.data = {};
}
this._readNeedUpdate = false;
}
/**
* @description: 将最新数据写入storage
*/
_updateData() {
const result = this.storage.setItem(this._namespaceKey, JSON.stringify(this.data));
if (result && result.code === 22) {
// 超出大小限制
if (!this._clearExpire()) {
// 无过期数据,开始按规则清除不重要数据
this.forceClearCount++;
this._clearCacheForce();
}
if (this.forceClearCount <= MAX_TRY_DELETE) {
this._updateData();
}
} else {
// 更新成功或非超限错误重置强制清除次数
this.forceClearCount = 0;
}
}
/**
* @description: 清除过期数据
* @return: {number} 清除成功的数量
*/
_clearExpire() {
let clearAmount = 0;
Object.keys(this.data).forEach((key) => {
const obj = this.data[key];
const now = Date.now();
if (typeof obj !== 'object' || (obj.e && obj.e < now)) {
clearAmount++;
delete this.data[key];
}
});
return clearAmount;
}
/**
* @description: storage空间不足,按规则清除缓存
*/
_clearCacheForce() {
let selectedKey = '';
Object.keys(this.data).forEach((key) => {
const obj = this.data[key];
if (typeof obj !== 'object') {
delete this.data[key];
return;
}
if (!selectedKey) {
selectedKey = key;
}
const selectedDate = this.data[selectedKey];
switch (this.shiftType) {
case 'FIFO':
if (!obj.t || obj.t < selectedDate.t) {
selectedKey = key;
}
break;
case 'FILO':
if (!obj.t || obj.t > selectedDate.t) {
selectedKey = key;
}
break;
case 'CE':
if (!selectedDate.e || (obj.e && obj.e < selectedDate.e)) {
selectedKey = key;
}
break;
}
});
delete this.data[selectedKey];
}
/**
* @description: 获取storage数据
* @return: {String|null}
*/
getItem(key) {
if (this._readNeedUpdate) {
this._getData();
}
const obj = this.data[key];
let value = null;
const now = Date.now();
if (typeof obj === 'object' && (!obj.e || obj.e >= now)) {
value = obj.v;
} else {
this.removeItem(key);
}
return value;
}
/**
* @description: set storage数据
* @param: {string} key
* @param: {any} value
* @param: {number?} maxAge 有效时长,毫秒,每次设置都会更新有效时长,sessionStorage设置无法突破sessionStorage的限制
*/
setItem(key, value, maxAge = null) {
const now = Date.now();
let expireTime = 0;
if (typeof maxAge === 'number') {
expireTime = now + maxAge;
}
// 单个数据存储格式
const obj = {
v: value, // 数据值
t: now, // 更新时间
e: expireTime, // 过期时间, 0代表不会过期
};
// localStorage写操作先同步缓存数据,防止多窗口写入出错
if (this.isLocalStorage) {
this._getData();
}
this.data[key] = obj;
this._updateData();
}
/**
* @description: 移除数据
* @param {string} key
*/
removeItem(key) {
// localStorage写操作先同步缓存数据,防止多窗口写入出错
if (this.isLocalStorage) {
this._getData();
}
delete this.data[key];
this._updateData();
}
/**
* @description: 清空storage
*/
clear() {
this.storage.clear();
}
/**
* @description: 清空单例空间数据
*/
clearNamespace() {
this.storage.removeItem(this._namespaceKey);
}
/**
* @description: 切换默认单例(默认值:NAMESPACE_KEY)key,建议单个项目全局只切换一次
* @param {string} namespaceKey 新的key
*/
changeNamespace(namespaceKey) {
this._namespaceKey = namespaceKey || NAMESPACE_KEY;
this._getData();
}
}
const sessionCache = new Storage({
isLocalStorage: false,
});
const localCache = new Storage({
isLocalStorage: true,
});
export { sessionCache, localCache };