深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

Fundebug 發表於 2019-04-08
Cookie localStorage

摘要: 對比Cookie、LocalStorage、sessionStorage與IndexedDB

Fundebug經授權轉載,版權歸原作者所有。

前言

隨著行動網路的發展與演化,我們手機上現在除了有原生 App,還能跑“WebApp”——它即開即用,用完即走。一個優秀的 WebApp 甚至可以擁有和原生 App 媲美的功能和體驗。WebApp 優異的效能表現,有一部分原因要歸功於瀏覽器儲存技術的提升。cookie儲存資料的功能已經很難滿足開發所需,逐漸被WebStorage、IndexedDB所取代,本文將介紹這幾種儲存方式的差異和優缺點。

想閱讀更多優質文章請猛戳GitHub部落格

一、Cookie

1. Cookie的來源

Cookie 的本職工作並非本地儲存,而是“維持狀態”

因為HTTP協議是無狀態的,HTTP協議自身不對請求和響應之間的通訊狀態進行儲存,通俗來說,伺服器不知道使用者上一次做了什麼,這嚴重阻礙了互動式Web應用程式的實現。在典型的網上購物場景中,使用者瀏覽了幾個頁面,買了一盒餅乾和兩瓶飲料。最後結帳時,由於HTTP的無狀態性,不通過額外的手段,伺服器並不知道使用者到底買了什麼,於是就誕生了Cookie。它就是用來繞開HTTP的無狀態性的“額外手段”之一。伺服器可以設定或讀取Cookies中包含資訊,藉此維護使用者跟伺服器會話中的狀態。

我們可以把Cookie 理解為一個儲存在瀏覽器裡的一個小小的文字檔案,它附著在 HTTP 請求上,在瀏覽器和伺服器之間“飛來飛去”。它可以攜帶使用者資訊,當伺服器檢查 Cookie 的時候,便可以獲取到客戶端的狀態。

在剛才的購物場景中,當使用者選購了第一項商品,伺服器在向使用者傳送網頁的同時,還傳送了一段Cookie,記錄著那項商品的資訊。當使用者訪問另一個頁面,瀏覽器會把Cookie傳送給伺服器,於是伺服器知道他之前選購了什麼。使用者繼續選購飲料,伺服器就在原來那段Cookie裡追加新的商品資訊。結帳時,伺服器讀取傳送來的Cookie就行了。

2. 什麼是Cookie及應用場景

Cookie指某些網站為了辨別使用者身份而儲存在使用者本地終端上的資料(通常經過加密)。 cookie是服務端生成,客戶端進行維護和儲存。通過cookie,可以讓伺服器知道請求是來源哪個客戶端,就可以進行客戶端狀態的維護,比如登陸後重新整理,請求頭就會攜帶登陸時response header中的set-cookie,Web伺服器接到請求時也能讀出cookie的值,根據cookie值的內容就可以判斷和恢復一些使用者的資訊狀態。

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

如上圖所示,Cookie 以鍵值對的形式存在

典型的應用場景有:

  • 記住密碼,下次自動登入。
  • 購物車功能。
  • 記錄使用者瀏覽資料,進行商品(廣告)推薦。

3. Cookie的原理及生成方式

Cookie的原理

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

第一次訪問網站的時候,瀏覽器發出請求,伺服器響應請求後,會在響應頭裡面新增一個Set-Cookie選項,將cookie放入到響應請求中,在瀏覽器第二次發請求的時候,會通過Cookie請求頭部將Cookie資訊傳送給伺服器,服務端會辨別使用者身份,另外,Cookie的過期時間、域、路徑、有效期、適用站點都可以根據需要來指定。

Cookie的生成方式主要有兩種:

  • 生成方式一:http response header中的set-cookie

我們可以通過響應頭裡的 Set-Cookie 指定要儲存的 Cookie 值。預設情況下,domain 被設定為設定 Cookie 頁面的主機名,我們也可以手動設定 domain 的值。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2018 07:28:00 GMT;//可以指定一個特定的過期時間(Expires)或有效期(Max-Age)
複製程式碼

當Cookie的過期時間被設定時,設定的日期和時間只與客戶端相關,而不是服務端。

  • 生成方式二:js中可以通過document.cookie可以讀寫cookie,以鍵值對的形式展示

例如我們在掘金社群控制檯輸入以下三句程式碼,便可以在Chrome 的 Application 皮膚檢視生成的cookie:

document.cookie="userName=hello"
複製程式碼
document.cookie="gender=male"
複製程式碼
document.cookie='age=20;domain=.baidu.com'
複製程式碼

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

從上圖中我們可以得出:

Domain 標識指定了哪些域名可以接受Cookie。如果沒有設定domain,就會自動繫結到執行語句的當前域。

如果設定為”.baidu.com”,則所有以”baidu.com”結尾的域名都可以訪問該Cookie,所以在掘金社群上讀取不到第三條程式碼儲存Cookie值。

4. Cookie的缺陷

  • Cookie 不夠大

Cookie的大小限制在4KB左右,對於複雜的儲存需求來說是不夠用的。當 Cookie 超過 4KB 時,它將面臨被裁切的命運。這樣看來,Cookie 只能用來存取少量的資訊。此外很多瀏覽器對一個站點的cookie個數也是有限制的。

這裡需注意:各瀏覽器的cookie每一個name=value的value值大概在4k,所以4k並不是一個域名下所有的cookie共享的,而是一個name的大小。

  • 過多的 Cookie 會帶來巨大的效能浪費

Cookie 是緊跟域名的。同一個域名下的所有請求,都會攜帶 Cookie。大家試想,如果我們此刻僅僅是請求一張圖片或者一個 CSS 檔案,我們也要攜帶一個 Cookie 跑來跑去(關鍵是 Cookie 裡儲存的資訊並不需要),這是一件多麼勞民傷財的事情。Cookie 雖然小,請求卻可以有很多,隨著請求的疊加,這樣的不必要的 Cookie 帶來的開銷將是無法想象的。

cookie是用來維護使用者資訊的,而域名(domain)下所有請求都會攜帶cookie,但對於靜態檔案的請求,攜帶cookie資訊根本沒有用,此時可以通過cdn(儲存靜態檔案的)的域名和主站的域名分開來解決。

  • 由於在HTTP請求中的Cookie是明文傳遞的,所以安全性成問題,除非用HTTPS。

5. Cookie與安全

對於 cookie 來說,我們還需要注意安全性。

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

HttpOnly 不支援讀寫,瀏覽器不允許指令碼操作document.cookie去更改cookie, 所以為避免跨域指令碼 (XSS) 攻擊,通過JavaScript的 Document.cookie API無法訪問帶有 HttpOnly 標記的Cookie,它們只應該傳送給服務端。如果包含服務端 Session 資訊的 Cookie 不想被客戶端 JavaScript 指令碼呼叫,那麼就應該為其設定 HttpOnly 標記。

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
複製程式碼

標記為 Secure 的Cookie只應通過被HTTPS協議加密過的請求傳送給服務端。但即便設定了 Secure 標記,敏感資訊也不應該通過Cookie傳輸,因為Cookie有其固有的不安全性,Secure 標記也無法提供確實的安全保障。

為了彌補 Cookie 的侷限性,讓“專業的人做專業的事情”,Web Storage 出現了。

HTML5中新增了本地儲存的解決方案----Web Storage,它分成兩類:sessionStorage和localStorage。這樣有了WebStorage後,cookie能只做它應該做的事情了——作為客戶端與伺服器互動的通道,保持客戶端狀態。

二、LocalStorage

1. LocalStorage的特點

  • 儲存的資料長期存在,下一次訪問該網站的時候,網頁可以直接讀取以前儲存的資料。
  • 大小為5M左右
  • 僅在客戶端使用,不和服務端進行通訊
  • 介面封裝較好

基於上面的特點,LocalStorage可以作為瀏覽器本地快取方案,用來提升網頁首屏渲染速度(根據第一請求返回時,將一些不變資訊直接儲存在本地)。

2.存入/讀取資料

localStorage儲存的資料,以“鍵值對”的形式存在。也就是說,每一項資料都有一個鍵名和對應的值。所有的資料都是以文字格式儲存。

存入資料使用setItem方法。它接受兩個引數,第一個是鍵名,第二個是儲存的資料。

localStorage.setItem("key","value");
複製程式碼

讀取資料使用getItem方法。它只有一個引數,就是鍵名。

var valueLocal = localStorage.getItem("key");
複製程式碼

具體步驟,請看下面的例子:

<script>
<script>
    if(window.localStorage){
      localStorage.setItem('name','world')
      localStorage.setItem(“gender','famale')
    }
</script>
複製程式碼
<body>
    <div id="name"></div>
    <div id="gender"></div>
    <script>
        var name = localStorage.getItem("name");
        var gender = localStorage.getItem("gender");
        document.getElementById("name").innerHTML = name;
        document.getElementById("gender").innerHTML = gender;
    </script>
</body>
複製程式碼

3. 使用場景

LocalStorage在儲存方面沒有什麼特別的限制,理論上 Cookie 無法勝任的、可以用簡單的鍵值對來存取的資料儲存任務,都可以交給 LocalStorage 來做。

這裡給大家舉個例子,考慮到 LocalStorage 的特點之一是持久,有時我們更傾向於用它來儲存一些內容穩定的資源。比如圖片內容豐富的電商網站會用它來儲存 Base64 格式的圖片字串:

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

給大家推薦一個好用的BUG監控工具Fundebug,歡迎免費試用!

三、sessionStorage

sessionStorage儲存的資料用於瀏覽器的一次會話,當會話結束(通常是該視窗關閉),資料被清空;sessionStorage 特別的一點在於,即便是相同域名下的兩個頁面,只要它們不在同一個瀏覽器視窗中開啟,那麼它們的 sessionStorage 內容便無法共享;localStorage 在所有同源視窗中都是共享的;cookie也是在所有同源視窗中都是共享的。除了儲存期限的長短不同,SessionStorage的屬性和方法與LocalStorage完全一樣。

1.sessionStorage的特點

  • 會話級別的瀏覽器儲存
  • 大小為5M左右
  • 僅在客戶端使用,不和服務端進行通訊
  • 介面封裝較好

基於上面的特點,sessionStorage 可以有效對錶單資訊進行維護,比如重新整理時,表單資訊不丟失。

2. 使用場景

sessionStorage 更適合用來儲存生命週期和它同步的會話級別的資訊。這些資訊只適用於當前會話,當你開啟新的會話時,它也需要相應的更新或釋放。比如微博的 sessionStorage就主要是儲存你本次會話的瀏覽足跡:

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

lasturl 對應的就是你上一次訪問的 URL 地址,這個地址是即時的。當你切換 URL 時,它隨之更新,當你關閉頁面時,留著它也確實沒有什麼意義了,乾脆釋放吧。這樣的資料用 sessionStorage 來處理再合適不過。

3.sessionStorage 、localStorage 和 cookie 之間的區別

  • 共同點:都是儲存在瀏覽器端,且都遵循同源策略。
  • 不同點:在於生命週期與作用域的不同

作用域:localStorage只要在相同的協議、相同的主機名、相同的埠下,就能讀取/修改到同一份localStorage資料。sessionStorage比localStorage更嚴苛一點,除了協議、主機名、埠外,還要求在同一視窗(也就是瀏覽器的標籤頁)下

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

生命週期:localStorage 是持久化的本地儲存,儲存在其中的資料是永遠不會過期的,使其消失的唯一辦法是手動刪除;而 sessionStorage 是臨時性的本地儲存,它是會話級別的儲存,當會話結束(頁面被關閉)時,儲存內容也隨之被釋放。

Web Storage 是一個從定義到使用都非常簡單的東西。它使用鍵值對的形式進行儲存,這種模式有點類似於物件,卻甚至連物件都不是——它只能儲存字串,要想得到物件,我們還需要先對字串進行一輪解析。

說到底,Web Storage 是對 Cookie 的擴充,它只能用於儲存少量的簡單資料。當遇到大規模的、結構複雜的資料時,Web Storage 也愛莫能助了。這時候我們就要清楚我們的終極大 boss——IndexedDB!

四、IndexedDB

IndexedDB 是一種低階API,用於客戶端儲存大量結構化資料(包括檔案和blobs)。該API使用索引來實現對該資料的高效能搜尋。IndexedDB 是一個執行在瀏覽器上的非關係型資料庫。既然是資料庫了,那就不是 5M、10M 這樣小打小鬧級別了。理論上來說,IndexedDB 是沒有儲存上限的(一般來說不會小於 250M)。它不僅可以儲存字串,還可以儲存二進位制資料。

1. IndexedDB的特點

  • 鍵值對儲存。

IndexedDB 內部採用物件倉庫(object store)存放資料。所有型別的資料都可以直接存入,包括 JavaScript 物件。物件倉庫中,資料以"鍵值對"的形式儲存,每一個資料記錄都有對應的主鍵,主鍵是獨一無二的,不能有重複,否則會丟擲一個錯誤。

  • 非同步

IndexedDB 操作時不會鎖死瀏覽器,使用者依然可以進行其他操作,這與 LocalStorage 形成對比,後者的操作是同步的。非同步設計是為了防止大量資料的讀寫,拖慢網頁的表現。

  • 支援事務。

IndexedDB 支援事務(transaction),這意味著一系列操作步驟之中,只要有一步失敗,整個事務就都取消,資料庫回滾到事務發生之前的狀態,不存在只改寫一部分資料的情況。

  • 同源限制

IndexedDB 受到同源限制,每一個資料庫對應建立它的域名。網頁只能訪問自身域名下的資料庫,而不能訪問跨域的資料庫。

  • 儲存空間大

IndexedDB 的儲存空間比 LocalStorage 大得多,一般來說不少於 250MB,甚至沒有上限。

  • 支援二進位制儲存。

IndexedDB 不僅可以儲存字串,還可以儲存二進位制資料(ArrayBuffer 物件和 Blob 物件)。

2. IndexedDB的常見操作

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

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

這條指令並不會返回一個DB物件的控制程式碼,我們得到的是一個IDBOpenDBRequest物件,而我們希望得到的DB物件在其result屬性中

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

除了result,IDBOpenDBRequest介面定義了幾個重要屬性:

onerror: 請求失敗的回撥函式控制程式碼

onsuccess:請求成功的回撥函式控制程式碼

onupgradeneeded:請求資料庫版本變化控制程式碼

<script>
    function openDB(name) {
        var request = window.indexedDB.open(name); //建立開啟IndexedDB
        request.onerror = function(e) {
            console.log("open indexdb error");
        };
        request.onsuccess = function(e) {
            myDB.db = e.target.result; //這是一個 IDBDatabase物件,這就是IndexedDB物件
            console.log(myDB.db); //此處就可以獲取到db例項
        };
    }
    var myDB = {
        name: "testDB",
        version: "1",
        db: null
    };
    openDB(myDB.name);
</script>
複製程式碼

控制檯得到一個 IDBDatabase物件,這就是IndexedDB物件

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

  • 關閉IndexedDB----indexdb.close()
function closeDB(db){
    db.close();
}
複製程式碼
  • 刪除IndexedDB----window.indexedDB.deleteDatabase(indexdb)
function deleteDB(name) {
  indexedDB.deleteDatabase(name)
}
複製程式碼

3. WebStorage、cookie 和 IndexedDB之間的區別

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB

從上表可以看到,cookie 已經不建議用於儲存。如果沒有大量資料儲存需求的話,可以使用 localStorage 和 sessionStorage 。對於不怎麼改變的資料儘量使用 localStorage 儲存,否則可以用 sessionStorage 儲存。

總結

正是瀏覽器儲存、快取技術的出現和發展,為我們的前端應用帶來了無限的轉機。近年來基於儲存、快取技術的第三方庫層出不絕,此外還衍生出了 PWA 這樣優秀的 Web 應用模型。總結下本文幾個核心觀點:

  • Cookie 的本職工作並非本地儲存,而是“維持狀態”
  • Web Storage 是 HTML5 專門為瀏覽器儲存而提供的資料儲存機制,不與服務端發生通訊
  • IndexedDB 用於客戶端儲存大量結構化資料

參考文章

關於Fundebug

Fundebug專注於JavaScript、微信小程式、微信小遊戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟體、百姓網等眾多品牌企業。歡迎大家免費試用

深入瞭解瀏覽器儲存:對比Cookie、LocalStorage、sessionStorage與IndexedDB