基於 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或者其餘。vue

  1. 有了基本骨架,咱們就能夠實現基本功能的封裝,這裏咱們先在原型中加一個屬性,來列出數據操做中的各個狀態。
status: {
 SUCCESS: 0, // 成功
 FAILURE: 1, // 失敗
 OVERFLOW: 2, // 數據溢出
 TIMEOUT: 3  // 超時
},
複製代碼

爲了實現過時時間,咱們有兩種思路,第一種是先將一個過時時間存到storage中,每次操做都檢查一遍是否過時,可是這種方案意味着對不一樣的鍵就要設置不一樣的過時時間的storage與之對應,這樣會佔用額外的庫內存,維護起來也不方便。另外一種方法就是將過時時間存放到鍵值中,將時間和值經過標識符分隔,每次取的時候從值中截取過時時間,再將真實的值取出來返回,這種方案不會添加額外的鍵值對存儲,維護起來也相對簡單,因此咱們採用這種方案。 爲了區分不一樣的庫對象,咱們還能夠添加鍵前綴,以下:算法

const BaseLocalStorage = function(preId, timeSign){
   this.preId = preId; // 鍵前綴
   this.timeSign = timeSign || '|-|';  // 過時時間和值的分隔符
 }
複製代碼

基於這個思想,咱們就能夠接下來的實現了。vuex

  • 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包裹,這樣能夠避免影響後面的邏輯。vue-cli

接下來咱們能夠這麼使用:typescript

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)
 
複製代碼

你們也能夠基於此擴展更強大的功能,若是有更好的想法,歡迎交流,探討。gulp

更多推薦

歡迎關注下方公衆號,獲取更多前端知識精粹學習社羣api

在公衆號點擊進羣,能夠加入vue學習小組,一塊兒學習前端技術;

回覆學習路徑,將獲取筆者多年從業經驗的前端學習路徑的思惟導圖;數組

交流微信羣:微信

相關文章
相關標籤/搜索