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

徐小夕 等级 680 0 0

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

设计思路

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

我们将基于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)

大家也可以基于此扩展更强大的功能,如果有更好的想法,欢迎交流,探讨。

更多推荐

收藏
评论区

相关推荐

基于 localStorage 实现一个具有过期时间的 DAO 库
本文主要解决原生localStorage无法设置过期时间的问题,并通过封装,来实现一个操作便捷,功能强大的localStorage库,关于库封装的一些基本思路和模
浏览器缓存库设计总结(localStorage/indexedDB)
前言 浏览器缓存设计一直是web性能优化中非常重要的一个环节,也是SPA应用盛行的今天不得不考虑的问题.作为一名优秀的前端工程师,为了让我们的应用更流畅,用户体验更好,我们有必要做好浏览器缓存策略. 每个Web应用体验都必须快速,对于渐进式 Web 应用更是如此。快速是指在屏幕上获取有意义内容所需的时间,要在不到 5 秒的时间内提供交互式体验。并且,它必
lz
原文链接: [lz-string 面向localstorage的字符串压缩库](https://my.oschina.net/ahaoboy/blog/4696653) [https://www.npmjs.com/package/lz-string](https://www.oschina.net/action/GoToLink?url=https%3A
DAO 四个包的建立
一、DAO 四个包的建立,降低代码之间的耦合性?   之前写代码,都是在一个包下。代码耦合性较高,不利于后期的维护。    dao(代码分层?) 有利于后期的维护代码,修改方便。 com.aaa.dao 存放dao相关的类型 处理 数据库的链接 存取数据。 com.aaa.servlet 存放serv
DAO设计模式
jsp只关注于数据的显示,而不关心数据是从哪里来,所以jsp中不应该使用任何sql包,数据库操作代码最好使用PreparedStatement。 j2ee的组件层次:客户端-->表示层-->业务层-->数据层 DAO属于j2ee数据层的操作,操作数据库,DAO封装了数据库中表的全部操作。 实例: 假设表:  create table person
J2EE之DAO设计模式及简单实现
**JAVAEE(Java Enterprise Edition ) 模式 : DAO模式** 因此在了解DAO模式之前,我们先来学习一下Java EE的体系结构: **(一)JavaEE体系结构** ![这里写图片描述](https://oscimg.oschina.net/oscnet/fcb9b1e4bb5d0e26c133a6494593
Java web
上次我们已经搞完了jsp的操作。现在该是后台的配置了。 在dao包里面进行数据链接:DBConn.java /**  *   */ /**  * @author Administrator  *  */ package dao; import java.sql.*;
Java 全能高效 MVC & RESTful 开发框架 Portal
**Portal-Basic 是一套功能完备的高性能 Full-Stack Web 应用开发框架,内置可扩展的 MVC Web 基础架构和 DAO 数据库访问组件(内部已提供了 Hibernate、MyBatis 与 JDBC DAO 组件),集成了 Action 拦截、Form / Dao / Spring Bean 装配、国际化、文件上传下载和页面静态化
java中的VO DTO DAO
VO是跟数据库里表的映射,一个表对应一个VO DAO是用VO来访问真实的表,对数据库的操作都在DAO中完成 BO是业务层,做逻辑处理的 VO , PO , BO , QO, DAO ,POJO O/R Mapping 是 Object Relational Mapping (对象关系映射)的缩写。 通俗点讲,就是将对象与关系数据库绑定,用对象来表示
219. 单页应用 会话管理(session、cookie、jwt)
> 原文链接:[https://github.com/ly525/blog/issues/219](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fly525%2Fblog%2Fissues%2F219) 关键字:`http-only`, `cookie`,`se
219. 单页应用 会话管理(session、cookie、jwt)
> 原文链接:[https://github.com/ly525/blog/issues/219](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fly525%2Fblog%2Fissues%2F219) 关键字:`http-only`, `cookie`,`se
Action层, Service层 和 Dao层的功能区分
1. **Action/Service/DAO简介:** Action是管理业务(Service)调度和管理跳转的。  Service是管理具体的功能的。  Action只负责管理,而Service负责实施。  DAO只完成增删改查,虽然可以1-n,n-n,1-1关联,模糊、动态、子查询都可以。但是无论多么复杂的查询,dao
Mybatis中如何将POJO作为参数传入sql
今天在工作时,需要将获取的用户的注册信息插入数据库,开始的做法是将所有的model的属性作为DAO接口的参数,后来想想不对劲,要是有100个属性,那我这个接口岂不是要有100个参数传进来? 于是我就考虑将整个Model或者DTO作为DAO接口的参数,向SQL传参,具体的实现如下:     (1)定义DAO接口: 1 @Repository
SessionStorage、LocalStorage详解
> 转载请注明出处:[葡萄城官网](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.grapecity.com.cn%2F),葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 > > 原文出处:https://blog.bitsrc.io/sessionstorage
Spring 对 DAO 的支持
**一、Spring 的 DAO 理念** DAO(Data Access Object)是用于访问数据的对象,虽然在大多数情况下将数据保存在数据库中,但这并不是唯一的选择,也可以将数据存储到文件中或 LDAP 中。DAO 不但屏蔽了数据存储的最终介质的不同,也屏蔽了具体的实现技术的不同。 早期,JDBC 是访问数据库的主流选择。近几年,数据持久化技术获