当前位置: 首页 > 工具软件 > Storage.js > 使用案例 >

js storage

苗承
2023-12-01

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 };

 

 类似资料:

相关阅读

相关文章

相关问答