本地存儲—— Cookie 到 Web Storage、IndexDB

手機上如今除了有原生 App,還能跑「WebApp」——它即開即用,用完即走。一個優秀的 WebApp 甚至能夠擁有和原生 App 媲美的功能和體驗。前端

我認爲,WebApp 就是咱們前端性能優化的產物,是咱們前端工程師對體驗不懈追求的結果,是 Web 網頁在性能上向 Native 應用的一次「宣戰」。數據庫

WebApp 優異的性能表現,要歸功於瀏覽器存儲技術的普遍應用——這其中除了咱們上節提到的緩存,本地存儲技術也功不可沒。瀏覽器

故事的開始:從 Cookie 提及

Cookie 的本職工做並不是本地存儲,而是「維持狀態」。緩存

在 Web 開發的早期,人們亟需解決的一個問題就是狀態管理的問題:HTTP 協議是一個無狀態協議,服務器接收客戶端的請求,返回一個響應,故事到此就結束了,服務器並無記錄下關於客戶端的任何信息。那麼下次請求的時候,如何讓服務器知道「我是我」呢?性能優化

在這樣的背景下,Cookie 應運而生。bash

Cookie 說白了就是一個存儲在瀏覽器裏的一個小小的文本文件,它附着在 HTTP 請求上,在瀏覽器和服務器之間「飛來飛去」。它能夠攜帶用戶信息,當服務器檢查 Cookie 的時候,即可以獲取到客戶端的狀態。服務器

關於 Cookie 的詳細內容,咱們能夠在 Chrome 的 Application 面板中查看到:前端工程師

如你們所見,Cookie 以鍵值對的形式存在dom

Cookie的性能劣勢

Cookie 不夠大

你們知道,Cookie 是有體積上限的,它最大隻能有 4KB。當 Cookie 超過 4KB 時,它將面臨被裁切的命運。這樣看來,Cookie 只能用來存取少許的信息。前端性能

過量的 Cookie 會帶來巨大的性能浪費

Cookie 是緊跟域名的。咱們經過響應頭裏的 Set-Cookie 指定要存儲的 Cookie 值。默認狀況下,domain 被設置爲設置 Cookie 頁面的主機名,咱們也能夠手動設置 domain 的值:

Set-Cookie: name=xiuyan; domain=xiuyan.me
複製代碼

同一個域名下的全部請求,都會攜帶 Cookie。你們試想,若是咱們此刻僅僅是請求一張圖片或者一個 CSS 文件,咱們也要攜帶一個 Cookie 跑來跑去(關鍵是 Cookie 裏存儲的信息我如今並不須要),這是一件多麼勞民傷財的事情。Cookie 雖然小,請求卻能夠有不少,隨着請求的疊加,這樣的沒必要要的 Cookie 帶來的開銷將是沒法想象的。

隨着前端應用複雜度的提升,Cookie 也漸漸演化爲了一個「存儲多面手」——它不只僅被用於維持狀態,還被塞入了一些亂七八糟的其它信息,被迫承擔起了本地存儲的「重任」。在沒有更好的本地存儲解決方案的年代裏,Cookie 小小的身體裏承載了 4KB 內存所不能承受的壓力。

爲了彌補 Cookie 的侷限性,讓「專業的人作專業的事情」,Web Storage 出現了。

向前一步:Web Storage

Web Storage 是 HTML5 專門爲瀏覽器存儲而提供的數據存儲機制。它又分爲 Local Storage 與 Session Storage。這兩組概念很是相近,咱們不妨先理解它們之間的區別,再對它們的共性進行研究。

Local Storage 與 Session Storage 的區別

二者的區別在於生命週期做用域的不一樣。

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

  • 做用域:Local Storage、Session Storage 和 Cookie 都遵循同源策略。但 Session Storage 特別的一點在於,即使是相同域名下的兩個頁面,只要它們不在同一個瀏覽器窗口中打開,那麼它們的 Session Storage 內容便沒法共享。

Web Storage 的特性

  • 存儲容量大: Web Storage 根據瀏覽器的不一樣,存儲容量能夠達到 5-10M 之間。

  • 僅位於瀏覽器端,不與服務端發生通訊。

Web Storage 核心 API 使用示例

Web Storage 保存的數據內容和 Cookie 同樣,是文本內容,以鍵值對的形式存在。Local Storage 與 Session Storage 在 API 方面無異,這裏咱們以 localStorage 爲例:

  • 存儲數據:setItem()
localStorage.setItem('user_name', 'xiuyan')

複製代碼
  • 讀取數據: getItem()
localStorage.getItem('user_name')

複製代碼
  • 刪除某一鍵名對應的數據: removeItem()
localStorage.removeItem('user_name')

複製代碼
  • 清空數據記錄:clear()
localStorage.clear()

複製代碼

應用場景

Local Storage

Local Storage 在存儲方面沒有什麼特別的限制,理論上 Cookie 沒法勝任的、能夠用簡單的鍵值對來存取的數據存儲任務,均可以交給 Local Storage 來作。

這裏給你們舉個例子,考慮到 Local Storage 的特色之一是持久,有時咱們更傾向於用它來存儲一些內容穩定的資源。好比圖片內容豐富的電商網站會用它來存儲 Base64 格式的圖片字符串:

有的網站還會用它存儲一些不常常更新的 CSS、JS 等靜態資源。

Session Storage

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

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

這樣看來,Web Storage 確實也夠強大了。那麼 Web Storage 是否能 hold 住全部的存儲場景呢?

答案是否認的。你們也看到了,Web Storage 是一個從定義到使用都很是簡單的東西。它使用鍵值對的形式進行存儲,這種模式有點相似於對象,卻甚至連對象都不是——它只能存儲字符串,要想獲得對象,咱們還須要先對字符串進行一輪解析。

說到底,Web Storage 是對 Cookie 的拓展,它只能用於存儲少許的簡單數據。當遇到大規模的、結構複雜的數據時,Web Storage 也心有餘而力不足了。這時候咱們就要清楚咱們的終極大 boss——IndexDB!

終極形態:IndexDB

IndexDB 是一個運行在瀏覽器上的非關係型數據庫。既然是數據庫了,那就不是 5M、10M 這樣小打小鬧級別了。理論上來講,IndexDB 是沒有存儲上限的(通常來講不會小於 250M)。它不只能夠存儲字符串,還能夠存儲二進制數據。

IndexDB 從推出之日起,其優質教程就層出不絕,咱們今天再也不着重講解它的詳細操做。接下來,咱們遵循 MDN 推薦的操做模式,經過一個基本的 IndexDB 使用流程,旨在對 IndexDB 造成一個感性的認知:

  1. 打開/建立一個 IndexDB 數據庫(當該數據庫不存在時,open 方法會直接建立一個名爲 xiaoceDB 新數據庫)。
// 後面的回調中,咱們能夠經過event.target.result拿到數據庫實例
  let db
  // 參數1位數據庫名,參數2爲版本號
  const request = window.indexedDB.open("xiaoceDB", 1)
  // 使用IndexDB失敗時的監聽函數
  request.onerror = function(event) {
     console.log('沒法使用IndexDB')
   }
  // 成功
  request.onsuccess  = function(event){
    // 此處就能夠獲取到db實例
    db = event.target.result
    console.log("你打開了IndexDB")
  }

複製代碼
  1. 建立一個 object store(object store 對標到數據庫中的「表」單位)。
// onupgradeneeded事件會在初始化數據庫/版本發生更新時被調用,咱們在它的監聽函數中建立object store
request.onupgradeneeded = function(event){
  let objectStore
  // 若是同名表未被建立過,則新建testif (!db.objectStoreNames.contains('test')) {
    objectStore = db.createObjectStore('test', { keyPath: 'id' })
  }
}  

複製代碼
  1. 構建一個事務來執行一些數據庫操做,像增長或提取數據等。
// 建立事務,指定表格名稱和讀寫權限
  const transaction = db.transaction(["test"],"readwrite")
  // 拿到Object Store對象
  const objectStore = transaction.objectStore("test")
  // 向表格寫入數據
  objectStore.add({id: 1, name: 'xiuyan'})

複製代碼
  1. 經過監聽正確類型的事件以等待操做完成。
// 操做成功時的監聽函數
  transaction.oncomplete = function(event) {
    console.log("操做成功")
  }
  // 操做失敗時的監聽函數
  transaction.onerror = function(event) {
    console.log("這裏有一個Error")
  }
  

複製代碼

IndexDB 的應用場景

經過上面的示例你們能夠看出,在 IndexDB 中,咱們能夠建立多個數據庫,一個數據庫中建立多張表,一張表中存儲多條數據——這足以 hold 住複雜的結構性數據。IndexDB 能夠看作是 LocalStorage 的一個升級,當數據的複雜度和規模上升到了 LocalStorage 沒法解決的程度,咱們毫無疑問能夠請出 IndexDB 來幫忙。

小結

瀏覽器緩存/存儲技術的出現和發展,爲咱們的前端應用帶來了無限的起色。近年來基於緩存/存儲技術的第三方庫層出不絕,此外還衍生出了 PWA 這樣優秀的 Web 應用模型。能夠說,現代前端應用,尤爲是移動端應用,之因此能夠發展到在體驗上叫板 Native 的地步,主要就是仰仗緩存/存儲立下的汗馬功勞。

相關文章
相關標籤/搜索