一文梳理Web存儲,從cookie,WebStorage到IndexedDB

前言

HTTP是無狀態的協議,網路早期最大的問題之一是如何管理狀態。伺服器無法知道兩個請求是否來自同一個瀏覽器。cookie應運而生,開始出現在各大網站,然而隨著前端應用複雜度的提高,Cookie 也漸漸演化為了一個「存儲多面手」,承載了 自身僅有的4KB 記憶體所不能承受的壓力。在這樣的背景下,web Storage應運而生,專門用於瀏覽器存儲。但web Storage也僅僅是cookie的擴展,只能用於存儲少量的簡單數據,當遇到大規模的、結構複雜的數據時,Web Storage 也愛莫能助。這時候,就不得不其強大的IndexedDB了,一個運行在瀏覽器上的非關係型資料庫。本文從cookie講起,Web Storage過渡,IndexedDB結尾,梳理一下web 存儲的體系與知識,希望在幫助作者梳理知識體系的同時也能幫助到廣大前端ers!

由於http是無狀態的協議,一旦客戶端和伺服器的數據交換完畢,就會斷開連接,再次請求,會重新連接,伺服器單從網路連接上無法知道用戶身份。怎麼辦呢?那就給每次新的用戶請求時,給它頒發一個身份證(獨一無二)吧,下次訪問,必須帶上身份證,這樣伺服器就會知道是誰來訪問了,針對不同用戶,做出不同的響應,這就是Cookie的原理。

Cookie 的本職工作並非本地存儲,而是「維持狀態」。它是瀏覽器存儲在用戶機器的一個小文本文件,大小不能超過4k,並且一些瀏覽器甚至會限制cookie的數量。Cookie是純文本,沒有可執行程式碼。儲存一些伺服器需要的資訊,每次請求站點,會發送相應的cookie,這些cookie可以用來辨別用戶身份資訊等作用。

cookie的編碼方式為encodeURI()。

cookie類型

cookie按照過期時間分為兩類:會話cookie和持久cookie。

會話cookie是一種臨時cookie,對標session,當用戶退出瀏覽器,會話cookie就會被刪除。默認情況下,即不設置過期時間,設置的cookie為會話cookie。持久cookie則會儲存在硬碟里,保留時間更長,不以瀏覽器的關閉為轉移,通常是持久性的cookie會維護某一個用戶周期性訪問伺服器的配置文件或者登錄資訊。

cookie的屬性

domain(cookie的域)

產生Cookie的伺服器可以向set-Cookie響應首部添加一個domain屬性來控制哪些站點可以看到那個cookie,例如下面:

Set-Cookie:name="losstie";domain="m.baidu.com"

如果用戶訪問m.baidu.com就會發送這個cookie,不是則不會。

path(cookie的路徑 )

path 參數告訴瀏覽器 cookie 的路徑。默認情況下,cookie 屬於當前頁面。

secure

設置了屬性secure,cookie只有在https協議加密情況下才會發送給服務端。但是這並不是最安全的,由於其固有的不安全性,敏感資訊也是不應該通過cookie傳輸的。

HttpOnly

禁止javascript操作cookie(為避免跨域腳本(xss)攻擊,通過javascript的document.cookie無法訪問帶有HttpOnly標記的cookie。)

SamSite

Cookie 的SameSite屬性用來限制第三方 Cookie,從而減少安全風險。它可以設置三個值:

  • Strict 完全禁止第三方 Cookie,跨站點時,任何情況下都不會發送 Cookie。換言之,只有當前網頁的 URL 與請求目標一致,才會帶上 Cookie。
  • Lax 規則稍稍放寬,大多數情況也是不發送第三方 Cookie,但是導航到目標網址的 Get 請求除外。導航到目標網址的 GET 請求,只包括三種情況:鏈接,預載入請求,GET 表單
  • None Chrome 計劃將Lax變為默認設置。這時,網站可以選擇顯式關閉SameSite屬性,將其設為None。不過,前提是必須同時設置Secure屬性(Cookie 只能通過 HTTPS 協議發送),否則無效。

cookie操作

通過docuemnt.cookie可以設置和獲取Cookie的值

/* cookie */
function set(key, value){
    document.cookie = key + "=" + value;
}

function get(key){
    var all = document.cookie;
    var s = all.indexOf(key)+key.toString().length+1;
    var e = all.indexOf(";",s);
    return all.substring(s,e);

}

function remove(key){
    var e = "expires=" + new Date(1970,1,1); // 標註過去的時間
    document.cookie = key + "=;" + e;
}

第三方cookie

通常cookie的域和瀏覽器地址的域匹配,這被稱為第一方cookie。第三方cookie就是cookie的域和地址欄中的域不匹配,這種cookie通常被用在第三方廣告網站。用於跟蹤用戶的瀏覽記錄,並且根據收集的用戶的瀏覽習慣,給用戶推送相關的廣告。

cookie的劣勢

  • Cookie 不夠大,體積不能超過4k,當 Cookie 超過 4KB 時,它將面臨被裁切的命運。這樣看來,Cookie 只能用來存取少量的資訊。
  • 每次都會攜帶在http頭中,過量的cookie會損耗性能。
  • cookie是緊跟域名的,同一個域名下的所有請求,都會攜帶 Cookie。
  • 不夠安全,伺服器沒法分辨用戶和攻擊者,攻擊者可以讀取網路上的其他用戶的資訊,包含HTTP Cookie的全部內容,以便進行中間的攻擊。使用跨站點腳本技術可以竊取cookie等。

Web Storage

Internet Explorer 8+, Firefox, Opera, Chrome, 和 Safari支援Web 存儲。

Web Storage 是 HTML5 專門為瀏覽器存儲而提供的數據存儲機制。它又分為 Local Storage 與 Session Storage。localStorage與sessionStorage保存的數據,以「鍵值對」的形式存在,並都是以文本格式保存。

localStorage與sessionStorage的區別

兩者的區別在於生命周期與作用域的不同。

  • 生命周期: Local Storage 是持久化的本地存儲,存儲在其中的數據永遠不會過期,只能是手動刪除。Session Storage 是臨時性的本地存儲,它是會話級別的存儲,當會話結束(頁面被關閉)時,存儲內容也隨之被釋放。
  • 作用域:Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 在遵循同源策略的前提下,還需要在同一窗口。只要它們不在同一個瀏覽器窗口中打開,那麼它們的 Session Storage 內容便無法共享。

方法

不管是 localStorage,還是 sessionStorage,可使用的API都相同。以localStorage為例:

  • 保存數據:localStorage.setItem(key,value);
  • 讀取數據:localStorage.getItem(key);
  • 刪除單個數據:localStorage.removeItem(key);
  • 刪除所有數據:localStorage.clear();
  • 得到某個索引的key:localStorage.key(index);
module.exports = {
    set:set,
    get:get,
    remove:remove,
    removeAll:removeAll,
    each:each
}


function set(key ,val){
    localStorage.setItem(key, JSON.stringify(val));
};

function get(key){
    return JSON.parse(localStorage.getItem(key));
};

function remove(key){
    localStorage.removeItem(key);
};


function removeAll(){
    localStorage.clear();
};


function each(fn){
    pluck(localStorage, function(val, key){
        fn(val, key);
        return false;
    });
}


function pluck(obj, fn){
    if(isList(obj)) {
        for(var i = 0; i<obj.length;i++){
            if(fn(obj[i], i)) {
                return obj[i];
            }
        }
    } else {
        for(var key in obj) {
            if(obj.hasOwnProperty(key)){

                if(fn(obj[key], key)){

                    return obj[key];
                }
            }
        }
    }
}

function isList(val) {
	return (val != null && typeof val != 'function' && typeof val.length == 'number')
}

function isFunction(val) {
	return val && Object.prototype.toString.call(val) === '[object Function]'
}

function isObject(val) {
	return val && Object.prototype.toString.call(val) === '[object Object]'
}

Web Storage 特點

  • 存儲容量大: Web Storage 根據瀏覽器的不同,存儲容量可以達到 5-10M 之間。Chrome、FireFox、Edge 都是 5M(IE 忽略)。
  • 僅位於瀏覽器端,不與服務端發生通訊。

應用場景

localStorage:

  • 快取靜態資源,比如圖片內容豐富的電商網站會用它來存儲 Base64 格式的圖片字元串或者存儲一些不經常更新的 CSS、JS 等靜態資源。
  • 作為前端 DB 的存儲介質

sessionStorage:

  • 用來存儲生命周期和它同步的會話級別的資訊。比如存儲用戶輸入的內容,當頁面刷新的時候可以立刻顯示出刷新前的內容

IndexedDB

IndexedDB 是一個運行在瀏覽器上的非關係型資料庫。理論上來說,IndexedDB 是沒有存儲上限的(一般來說不會小於 250M)。它不僅可以存儲字元串,還可以存儲二進位數據。

IndexedDB特點

  • 鍵值對儲存,IndexedDB 內部採用對象倉庫(object store)存放數據。
  • 非同步,ndexedDB 操作時不會鎖死瀏覽器,用戶依然可以進行其他操作(與 LocalStorage 形成對比,後者的操作是同步的)。
  • 支援事務,只要有一步失敗,整個事務就都取消,資料庫回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況。
  • 同源限制,每一個資料庫對應創建它的域名。網頁只能訪問自身域名下的資料庫,而不能訪問跨域的資料庫。
  • 存儲空間大,一般來說不少於 250MB,甚至沒有上限。
  • 支援二進位儲存,僅可以儲存字元串,還可以儲存二進位數據(ArrayBuffer 對象和 Blob 對象)。

常見操作

IndexedDB大部分操作並不是常用的調用方法,返回結果的模式,而是請求——響應的模式。

  • 建立打開IndexedDB ----window.indexedDB.open("testDB")

  • 關閉IndexedDB----indexdb.close()

  • 刪除IndexedDB----indexedDB.deleteDatabase(indexdb)

應用場景

在 IndexedDB 中,我們可以創建多個資料庫,一個資料庫中創建多張表,一張表中存儲多條數據——這足以 hold 住複雜的結構性數據。

  • 不需要網路連接的離線純應用,比如Todolist這類用來記錄待辦任務類型的應用。
  • 需要存儲大量數據的應用,比如圖書館管理系統這類存儲大量數據的應用;
  • 配合service work構建pwa應用,用於快取網路請求。

cookie/webStorage/IndexedDB區別

小結

瀏覽器存儲、快取技術的出現和發展,為前端應用帶來了無限的轉機,頁面越發複雜,功能越發強大。可以說,現代前端應用,尤其是移動端應用,之所以可以發展到在體驗上叫板 Native 的地步,web存儲功不可沒(還有快取)。

拓展學習

瀏覽器資料庫 IndexedDB 入門教程-阮一峰

深入了解瀏覽器存儲--從cookie到WebStorage、IndexedDB

Cookie 的 SameSite 屬性-阮一峰

我遇過的最難的Cookie問題

這一次帶你徹底了解Cookie