Web 前端實現本地存儲

當咱們在說起web前端本地存儲的時候,首先須要介紹一下本地化存儲的概念和歷史。本地化存儲歷來不是一個新奇的概念,由於web應用程序一直在追求的就是媲美甚至超越桌面應用程序。可是桌面應用程序一直優於web應用程序一個很重要的緣由是它的本地化存儲獲得了很好的支持。對於本地應用程序,操做系統會提供一個抽象層,用於存儲和獲取特定於應用程序的數據,這些數據能夠存儲於註冊表、INI文件,或者其餘什麼地方,這取決於操做系統的實現,若是本地應用程序須要不單是鍵值對形式的本地存儲,可使用嵌入式數據庫或其餘不少種解決方案。而對於web應用程序,它的本地存儲一步一步走到今天的HTML5本地存儲是很是不容易的。爲了描述它的歷史,咱們能夠先看一張圖片:前端

圖片描述
從圖片能夠看出,不管是從存儲數據的大小仍是兼容性來看,web前端本地存儲都走得不容易。在着重介紹HTML5本地存儲以前,咱們先來看一看前面幾個存儲方式的概念。git

  • HTTP cookie:HTTP cookie的缺點很明顯,最多隻能存儲4KB的數據,每一個HTTP請求都會被傳送回服務器,明文傳輸(除非你使用SSL)。
    在這裏簡單介紹一下Cookie。Cookie是爲了解決HTTP無狀態的特性而出現的,也能夠叫用戶識別機制。經常使用的用戶識別機制包括:

    • 承載用戶信息的HTTP首部
    • 客戶端IP地址追蹤技術,經過用戶的IP地址對其進行識別
    • 用戶登陸,用認證機制來識別用戶
    • 胖URL,一種在URL中嵌入識別信息的技術
    • cookie,一種強大且高效的持久身份識別技術

對於購物網站而言,cookie是很是重要的,爲了實現購物車功能,把已選物品加入cookie,能夠實現不一樣頁面之間數據的同步,同時在提交訂單的時候又會把這些cookie傳到後臺,大大方便了先後端開發github

  • userData是微軟在上世紀90年代的瀏覽器大戰時推出的本地存儲方案,藉助DHTML的behaviour屬性來存儲本地數據, 容許每一個頁面最多存儲64K數據,每一個站點最多640K數據,userData的缺點顯而易見,它不是Web標準的一部分,除非你的程序只須要支持IE, 不然它基本沒什麼用處。web

  • Flash cookie的名字有些誤導,它實際上和HTTP cookie並非一回事,或許它的名字應該叫作"Flash本地存儲」,Flash cookie默認容許每一個站點存儲不超過100K的數據,若是超出了,Flash會自動向用戶請求更大的存儲空間,藉助Flash的 ExternalInterface接口,你能夠很輕鬆地經過Javascript操做Flash的本地存儲。Flash的問題很簡單,就是由於它是 Flash。數據庫

  • Gears是Google在07年發佈的一個開源瀏覽器插件,旨在改進各大瀏覽器的兼容性,Gears內置了一個基於SQLite的嵌入式 SQL數據庫,並提供了統一API對數據庫進行訪問,在取得用戶受權以後,每一個站點能夠在SQL數據庫中存儲不限大小的數據,Gears的問題就是 Google本身都已經不用它了。後端

從上面的簡介咱們能夠看出,在之前,本地存儲面臨的主要問題是,對於存儲容量較大的方式,須要特定的插件支持;對於不須要插件支持的存儲方式,則處於安全問題或者大小限制而遭到扼殺。在這種雙重的矛盾面前,HTML5本地存儲橫空出世,對於前端開發人員是一種巨大的福音。瀏覽器

所謂的HTML5本地存儲更精確的說法應該是DOM存儲。根據MDN的定義,DOM存儲的機制是經過存儲字符串類型的鍵/值對,來提供一種安全的存取方式.這個附加功能的目標是提供一個全面的,能夠用來建立交互式應用程序的方法(包括那些高級功能,例如能夠離線工做一段時間)。安全

HTML5的DOM存儲分紅兩種:SessionStorage和LocalStorage。在當代瀏覽器中的兼容性以下:
圖片描述
上圖中說起的globalStorage是非標準的,已經廢棄,在這裏咱們直接忽略它。而sessionStorage和localStorage在絕大部分現代瀏覽器中已經獲得了很好的支持,可是既然是絕大部分,就必須照顧那些還不支持這兩個對象的瀏覽器。爲了檢測瀏覽器是否支持這兩個對象,咱們能夠簡單的用下面的代碼來檢測:服務器

function storageSupport() {  
        try {  
            return 'localStorage' in window && window['localStorage'] !== null;  
        } catch (e) {  
            return false;  
        }  
}

很是幸運的是,這兩種對象的使用方式都很是簡單,這裏借用網上 的一張圖:
圖片描述
首先咱們來看一看sessionStorage,sessionStorage 是個全局對象,它維護着在頁面會話(page session)期間有效的存儲空間。只要瀏覽器開着,頁面會話週期就會一直持續。當頁面從新載入(reload)或者被恢復(restores)時,頁面會話也是一直存在的。每在新標籤或者新窗口中打開一個新頁面,都會初始化一個新的會話。這句話看起來比較抽象,咱們直接看一個demo:cookie

var name = sessionStorage.setItem("myname","yuanzm");
 alert(sessionStorage.getItem("myname"));

當咱們在瀏覽器中打開的時候,就會彈出窗口,顯示「yuanzm」,而後咱們按F12鍵,查看瀏覽器的調試窗口:
圖片描述

咱們可以發現,在瀏覽器的本地存儲sessionStorage中已經有了key值爲「myname」的項。這個時候,刷新頁面,仍然會彈出「yuanzm」,由於若是不調用sessionStorage.removeItem()或者手動清除這個項的話,這個項將一直存在。而上面提到的「只要瀏覽器開着,頁面會話週期就會一直持續。當頁面從新載入(reload)或者被恢復(restores)時,頁面會話也是一直存在的。每在新標籤或者新窗口中打開一個新頁面,都會初始化一個新的會話」的意思是說,若是咱們不從新設置myname的值,在新打開一個瀏覽器標籤或者再次打開一個瀏覽器窗口的時候,這個值是不存在的,也就是null。爲了驗證這一點,很簡單,咱們把上面兩行代碼的第一行註釋掉,而後刷新頁面,接着在新的瀏覽器標籤中打開這個文件。這兩個動做分別會產生什麼效果呢?答案很簡單,當再次刷新頁面的時候,仍然會彈出「yuanzm」,由於這個數據已經保存在本地了,而修改代碼以後在新的頁面打開,獲得的結果是null,由於當前頁面會話中沒有「myname」這個值。

接下來咱們看一看localStorage,他是跨多個窗口,且持續範圍可超過當前會話;意味着當瀏覽器關閉再從新打開,數據依然是可用的;拿上面的例子來講,當修改代碼以後,在新的標籤打開頁面,仍然會彈出「yuanzm」,咱們再次在瀏覽器中查看效果:
圖片描述

 因爲這兩個對象的使用很簡單,暫時就介紹到這裏。下面還須要介紹一下的就是兼容性問題。緣由很簡單,由於並非全部的瀏覽器都支持這兩個對象。這裏的兼容包括兩種,第一種是在沒有原生支持localStorage的瀏覽器中使用,第二種是兼容不一樣瀏覽器對於這兩種用法的差別。對於第一種,MDN給出了兼容代碼:

if (!window.localStorage) {
  Object.defineProperty(window, "localStorage", new (function () {
    var aKeys = [], oStorage = {};
    Object.defineProperty(oStorage, "getItem", {
      value: function (sKey) { return sKey ? this[sKey] : null; },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "key", {
      value: function (nKeyId) { return aKeys[nKeyId]; },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "setItem", {
      value: function (sKey, sValue) {
        if(!sKey) { return; }
        document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
      },
      writable: false,
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "length", {
      get: function () { return aKeys.length; },
      configurable: false,
      enumerable: false
    });
    Object.defineProperty(oStorage, "removeItem", {
      value: function (sKey) {
        if(!sKey) { return; }
        var sExpDate = new Date();
        sExpDate.setDate(sExpDate.getDate() - 1);
        document.cookie = escape(sKey) + "=; expires=" + sExpDate.toGMTString() + "; path=/";
      },
      writable: false,
      configurable: false,
      enumerable: false
    });
    this.get = function () {
      var iThisIndx;
      for (var sKey in oStorage) {
        iThisIndx = aKeys.indexOf(sKey);
        if (iThisIndx === -1) { oStorage.setItem(sKey, oStorage[sKey]); }
        else { aKeys.splice(iThisIndx, 1); }
        delete oStorage[sKey];
      }
      for (aKeys; aKeys.length > 0; aKeys.splice(0, 1)) { oStorage.removeItem(aKeys[0]); }
      for (var iCouple, iKey, iCouplId = 0, aCouples = document.cookie.split(/\s*;\s*/); iCouplId < aCouples.length; iCouplId++) {
        iCouple = aCouples[iCouplId].split(/\s*=\s*/);
        if (iCouple.length > 1) {
          oStorage[iKey = unescape(iCouple[0])] = unescape(iCouple[1]);
          aKeys.push(iKey);
        }
      }
      return oStorage;
    };
    this.configurable = false;
    this.enumerable = true;
  })());
}

至於第二種,在github上面有不少優秀的代碼,博主這裏推薦其中一份:https://github.com/mortzdk/localStorage

相關文章
相關標籤/搜索