写点什么

基于 localStorage 实现一个具有过期时间的 DAO 库

用户头像
徐小夕
关注
发布于: 2020 年 12 月 06 日
基于 localStorage 实现一个具有过期时间的 DAO 库

本文主要解决原生 localStorage 无法设置过期时间的问题,并通过封装,来实现一个操作便捷,功能强大的 localStorage 库,关于库封装的一些基本思路和模式,我将采用之前写的如何用不到200行代码写一款属于自己的js类库中类似的方法,感兴趣的朋友可以学习,交流。

设计思路



我们将基于 localStorage 原始 api 进行扩展,让其支持失效时间,操作完成后的回调。在文章的最后,我将给出库的完成代码,接下来我们就一步步实现吧。

正文

  1. 首先,我们来设计库的基本框架:

 const BaseStorage = function(preId, timeSign){   // 初始化一些操作 }
BaseStorage.prototype = { storage: localStorage || window.localStorage, set: function(key, value, cb, time){ }, get: function(key, cb){ }, // 删除storage,如果删除成功,返回删除的内容 remove: function(key, cb){ } }复制代码
复制代码

如上可以发现,我们的 storage 会有三个核心 api,分别为 set,get,remove,我们使用 localStorage 作为基础库支持,当然你也可以将上面的库换成 sessionStorage 或者其他。

  1. 有了基本骨架,我们就可以实现基本功能的封装,这里我们先在原型中加一个属性,来列出数据操作中的各个状态。

status: { SUCCESS: 0, // 成功 FAILURE: 1, // 失败 OVERFLOW: 2, // 数据溢出 TIMEOUT: 3  // 超时},复制代码
复制代码

为了实现过期时间,我们有两种思路,第一种是先将一个过期时间存到 storage 中,每次操作都检查一遍是否过期,但是这种方案意味着对不同的键就要设置不同的过期时间的 storage 与之对应,这样会占用额外的库内存,维护起来也不方便。另一种方法就是将过期时间存放到键值中,将时间和值通过标识符分隔,每次取的时候从值中截取过期时间,再将真实的值取出来返回,这种方案不会添加额外的键值对存储,维护起来也相对简单,所以我们采用这种方案。为了区分不同的库对象,我们还可以添加键前缀,如下:

const BaseLocalStorage = function(preId, timeSign){   this.preId = preId; // 键前缀   this.timeSign = timeSign || '|-|';  // 过期时间和值的分隔符 }复制代码
复制代码

基于这个思想,我们就可以接下来的实现了。

  • getKey——修饰 key 的方法,不影响用户对真实 key 的影响

getKey: function(key){     return this.preId + key   },复制代码
复制代码
  • set 实现

set: function(key, value, cb, time){     var status = this.status.SUCCESS,     key = this.getKey(key);     // 设置失效时间,未设置时间默认为一个月     try{       time = new Date(time).getTime() || time.getTime();     }catch(e){       time = new Date().getTime() + 1000*60*60*24*31     }     try{       this.storage.setItem(key, time + this.timeSign + value);     }catch(e){       status = this.status.OVERFLOW;     }     // 操作完成后的回调     cb && cb.call(this, status, key, value)   }复制代码
复制代码
  • get 实现

get: function(key, cb){     var status = this.status.SUCCESS,     key = this.getKey(key),     value = null,     timeSignLen = this.timeSign.length,     that = this,     index,     time,     result;     try{       value = that.storage.getItem(key);     }catch(e){       result = {         status: that.status.FAILURE,         value: null       }       cb && cb.call(this, result.status, result.value);       return result     }     if(value) {       index = value.indexOf(that.timeSign);       time = +value.slice(0, index);       // 判断是否过期,过期则清除       if(time > new Date().getTime() || time == 0){         value = value.slice(index+timeSignLen);       }else{         value = null,         status = that.status.TIMEOUT;         that.remove(key);       }     }else{       status = that.status.FAILURE;     }     result = {       status: status,       value: value     };     cb && cb.call(this, result.status, result.value);     return result   }复制代码
复制代码
  • remove 实现

// 删除storage,如果删除成功,返回删除的内容   remove: function(key, cb){     var status = this.status.FAILURE,     key = this.getKey(key),     value = null;     try{       value = this.storage.getItem(key);     }catch(e){       // dosomething     }     if(value){       try{         this.storage.removeItem(key);         status = this.status.SUCCESS;       }catch(e){         // dosomething       }     }     cb && cb.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))   }复制代码
复制代码

在 api 的实现过程中,由于某种误操作很可能导致 storage 报错,所以建议最好用 trycatch 包裹,这样可以避免影响后面的逻辑。

接下来我们可以这么使用:

let a = new BaseStorage('_', '@');a.set('name', '123')a.get('name') // {status: 0, value: "123"}// 设置失效时间a.set('name', '123', null, new Date().getTime() + 1000*60*60*24*31)// 移除a.remove('name')复制代码
复制代码

完整源码

/** * 数据管理器 */(function(win){  const BaseStorage = function(preId, timeSign){    this.preId = preId;    this.timeSign = timeSign || '|-|';  }   BaseStorage.prototype = {    status: {      SUCCESS: 0,      FAILURE: 1,      OVERFLOW: 2,      TIMEOUT: 3    },    storage: localStorage || window.localStorage,    getKey: function(key){      return this.preId + key    },    set: function(key, value, cb, time){      var status = this.status.SUCCESS,      key = this.getKey(key);      // 设置失效时间,未设置时间默认为一个月      try{        time = new Date(time).getTime() || time.getTime();      }catch(e){        time = new Date().getTime() + 1000*60*60*24*31      }      try{        this.storage.setItem(key, time + this.timeSign + value);      }catch(e){        status = this.status.OVERFLOW;      }      cb && cb.call(this, status, key, value)    },    get: function(key, cb){      var status = this.status.SUCCESS,      key = this.getKey(key),      value = null,      timeSignLen = this.timeSign.length,      that = this,      index,      time,      result;      try{        value = that.storage.getItem(key);      }catch(e){        result = {          status: that.status.FAILURE,          value: null        }        cb && cb.call(this, result.status, result.value);        return result      }      if(value) {        index = value.indexOf(that.timeSign);        time = +value.slice(0, index);        if(time > new Date().getTime() || time == 0){          value = value.slice(index+timeSignLen);        }else{          value = null,          status = that.status.TIMEOUT;          that.remove(key);        }      }else{        status = that.status.FAILURE;      }      result = {        status: status,        value: value      };      cb && cb.call(this, result.status, result.value);      return result    },    // 删除storage,如果删除成功,返回删除的内容    remove: function(key, cb){      var status = this.status.FAILURE,      key = this.getKey(key),      value = null;      try{        value = this.storage.getItem(key);      }catch(e){        // dosomething      }      if(value){        try{          this.storage.removeItem(key);          status = this.status.SUCCESS;        }catch(e){          // dosomething        }      }      cb && cb.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))    }  }   win.BS = BaseStorage;})(window) 复制代码
复制代码

大家也可以基于此扩展更强大的功能,如果有更好的想法,欢迎微信搜索 趣谈前端 和我交流,探讨。


发布于: 2020 年 12 月 06 日阅读数: 338
用户头像

徐小夕

关注

前端架构师,致力于前端工程化,可视化 2020.08.08 加入

公众号《趣谈前端》.喜欢coding, 开源, 擅长领域react, vue, nodejs, 小程序, 小游戏, 热爱旅游, 健身, 微信Mr_xuxiaoxi

评论

发布
暂无评论
基于 localStorage 实现一个具有过期时间的 DAO 库