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: sessionLsotagethis.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模拟sessionStoragethis.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 };