前端儲存除了 localStorage 還有啥

阿寶哥 發表於 2020-06-16
前端 localStorage
本文介紹一些與前端資料儲存有關,有趣、好玩、有用的開源庫。除此之外,還會讓你掌握各種 Web 儲存方案的特點,趕緊來了解一下。

前端的資料儲存方式,你除了用過 Cookies、localStorage 和 sessionStorage 外,還有用過其它的儲存方式麼?其實除了前面提到的 3 種儲存方式,目前主流的瀏覽器還支援 Web SQL 和 IndexedDB。

目前市場上主流的瀏覽器有 Chrome 、Safari、Firefox、Opera、UC Browser 和 Internet Explorer 等,其中截止 2020 年 5 月,Chrome 的市場佔有率為 63.93%,遠遠超過第二名 Safari 瀏覽(18.19%)。

stat-counter-browser-ww-monthly.png

(圖片來源:https://gs.statcounter.com/

這裡我們以市場佔有率第一的 Chrome 瀏覽器為例,來了解一下它支援的所有儲存方案:

web-browser-storage.jpg

(開啟 Chrome 開發者工具,切換至 Application 欄位)

在介紹目前比較流行的一些開源的前端儲存方案之前,阿寶哥先分享一些與儲存有關,有趣好玩的開源庫。

一、有趣好玩的開源庫

1.1 Sharedb

Realtime database backend based on Operational Transformation (OT)。

https://github.com/share/sharedb

ShareDB 是一個基於 JSON 文件操作轉換(OT)的實時資料庫後端。它是 DerbyJS Web 應用程式框架的實時後端。

示例1:實時資料同步

simple-app-for-realtime-sync.gif

示例2:展示實時查詢的排行榜應用程式

leaderboard-for-live-queries.gif

1.2 ImmortalDB

🔩 A relentless key-value store for the browser。

https://github.com/gruns/Immo...

ImmortalDB 是在瀏覽器中儲存持久鍵值資料的最佳方法。儲存到 ImmortalDB 的資料被冗餘地儲存在 Cookies,IndexedDB 和 localStorage 中,並且如果其中的任何資料被刪除或損壞,它們將不斷進行自我修復。

例如,清除 Cookie 是一種常見的使用者操作,即使對於非技術使用者也是如此。在儲存壓力下,瀏覽器在沒有警告的情況下隨意刪除 IndexedDB、localStorage 或 sessionStorage。

示例

import { ImmortalDB } from 'immortal-db'

await ImmortalDB.set('name', 'semlinker'); // Set
await ImmortalDB.get('name', default='lolo'); // Get
await ImmortalDB.remove('name'); // Remove 

1.3 web-storage-cache

對 localStorage 和 sessionStorage 進行了擴充套件,新增了超時時間,序列化方法。

https://github.com/wuchangmin...

WebStorageCache 對 HTML5 localStorage 和 sessionStorage 進行了擴充套件,新增了超時時間,序列化方法。可以直接儲存 JSON 物件,同時可以非常簡單的進行超時時間的設定。

優化:WebStorageCache 自動清除訪問的過期資料,避免了過期資料的累積。另外也提供了清除全部過期資料的方法:wsCache.deleteAllExpires();

示例

var wsCache = new WebStorageCache();

// 快取字串'wqteam' 到 'username' 中, 超時時間100秒
wsCache.set('username', 'wqteam', {exp : 100});

// 超時截止日期,可用使用Date型別
var nextYear = new Date();
nextYear.setFullYear(nextYear.getFullYear() + 1);
wsCache.set('username', 'wqteam', {exp : nextYear});

// 獲取快取中 'username' 的值
wsCache.get('username');

// 快取簡單js物件,預設使用序列化方法為JSON.stringify。
// 可以通過初始化wsCache的時候配置serializer.serialize
wsCache.set('user', { name: 'Wu', organization: 'wqteam'});

1.4 lz-string

LZ-based compression algorithm for JavaScript。

https://github.com/pieroxy/lz...

lz-string 旨在滿足在 localStorage 中(尤其是在移動裝置上)儲存大量資料的需求。 localStorage 通常限制為 5MB ~10MB,你可以通過對資料進行壓縮,以儲存更多的資料。

示例

var string = "Hello, my name is semlinker";
console.log("Size of sample is: " + string.length);

var compressed = LZString.compress(string);
console.log("Size of compressed sample is: " + compressed.length);

string = LZString.decompress(compressed);
console.log("Sample is: " + string);

下圖是使用官方線上示例進行字串壓縮測試的結果:

lz-string-demo.jpg

(圖片來源:https://pieroxy.net/blog/page...

接下來我們開始來介紹一些主流的資料庫。

二、主流的資料庫

2.1 localForage

💾 Offline storage, improved. Wraps IndexedDB, WebSQL, or localStorage using a simple but powerful API.

https://github.com/localForag...

localForage 是一個快速簡單的 JavaScript 儲存庫。 它通過使用類似於 localStorage 的簡單 API 來使用非同步儲存(IndexedDB 或 WebSQL),進而改善你的 Web 應用程式的離線體驗。

對於不支援 IndexedDB 或 WebSQL 的瀏覽器,localForage 會使用 localStorage 進行資料儲存。此外,localForage 還支援儲存所有可以序列化為 JSON 的原生 JS 物件以及 ArrayBuffers,Blob 和 TypedArrays。

localForage 主要支援的平臺:

  • IE 10(IE 8+ 使用 localStorage)
  • Opera 15(Opera 10.5+ 使用 localStorage)
  • Firefox 18
  • Safari 3.1(包括 Mobile Safari)
  • Chrome 23、Chrome for Android 32
  • Phonegap/Apache Cordova 1.2.0

2.2 PouchDB

🐨 - PouchDB is a pocket-sized database.

https://github.com/pouchdb/po...

PouchDB 是一個瀏覽器內資料庫,允許應用程式在本地儲存資料,以便使用者即使在離線時也可以享受應用程式的所有功能。另外,資料在客戶端之間是同步的,因此使用者可以隨時隨地保持最新狀態。

PouchDB 也在 Node.js 中執行,可以用作與 CouchDB 相容的伺服器的直接介面。該 API 在每個環境中工作都是相同的,因此你可以花更少的時間來擔心瀏覽器的差異,而花更多的時間來編寫乾淨、一致的程式碼。

PouchDB 支援所有現代瀏覽器:

  • Firefox 29+ (Including Firefox OS and Firefox for Android)
  • Chrome 30+
  • Safari 5+
  • Internet Explorer 10+
  • Opera 21+
  • Android 4.0+
  • iOS 7.1+
  • Windows Phone 8+

PouchDB 在幕後使用 IndexedDB,若當前環境不支援 IndexedDB 則回退到 Web SQL。

2.3 Rxdb

💻 🔄 📱 A realtime Database for JavaScript Applications.

https://github.com/pubkey/rxdb

RxDB(Reactive Database 的縮寫)是 NoSQL 資料庫,用於 JavaScript 應用程式,如網站,混合應用程式,Electron Apps,Progressive Web Apps 和 Node.js。響應式意味著你不僅可以查詢當前狀態,還可以訂閱所有狀態更改,比如查詢的結果或文件的單個欄位。

rxdb-realtime.gif

這對於基於 UI 的實時應用程式非常有用,因為它易於開發,並且具有很大的效能優勢。為了在客戶端和伺服器之間複製資料,RxDB 提供了用於與任何 CouchDB 相容端點以及自定義 GraphQL 端點進行實時複製的模組。

RxDB 支援以下特性:

  • Mango-Query:支援 mquery API 從集合中獲取資料,支援鏈式的 mongoDB 查詢風格。
  • Replication:因為 RxDB 依賴於 PouchDB,因此很容易實現終端裝置與伺服器之間的資料同步。
  • Reactive:RxDB 使得同步 DOM 的狀態變得很簡單。
  • MultiWindow/Tab:當 RxDB 的兩個例項使用相同的儲存引擎,它們的狀態和操作流將會被廣播。這意味著對於兩個瀏覽器視窗,視窗 #1 的資料變化也會自動影響視窗 #2 的資料狀態。
  • Schema:通過 jsonschema 來定義 Schemas,它們用來描述資料格式。
  • Encryption:通過將模式欄位設定為encrypted,該欄位的值將以加密模式儲存,沒有密碼就無法讀取。

2.4 NeDB

The JavaScript Database, for Node.js, nw.js, electron and the browser.

https://github.com/louischatr...

NeDB 是一個 JavaScript 資料庫,能夠執行在 Node.js、nw.js、Electron 和瀏覽器環境。它是使用純的 JavaScript 實現,不依賴其它庫,提供的 API 是 MongoDB API 的子集,重要的是它的速度非常快:

  • 插入:10,680 ops/s
  • 查詢:43,290 ops/s
  • 更新:8,000 ops/s。
  • 刪除:11,750 ops/s。

ops (operation per second) 即表示每秒操作的次數。

2.5 Dexie.js

A Minimalistic Wrapper for IndexedDB.

https://github.com/dfahlander...

Dexie.js 是 IndexedDB 的包裝庫,它提供了一套經過精心設計的 API,強大的錯誤處理,較強的可擴充套件性,此外它能夠跟蹤資料變化,支援 KeyRange (搜尋不區分大小寫,可設定匹方式和 OR 操作)。

Dexie.js 主要為了解決原生 IndexedDB API 中存在的三個主要問題:

  • 異常錯誤處理。
  • 較弱的查詢功能。
  • 程式碼複雜性。

為了便於開發者接入 Dexie.js,在 Dexie.js 官網中提供了豐富的示例:

  • React + Dexie
  • React + Redux + Dexie
  • Dexie with Typescript
  • Angular + Dexie
  • Dexie with Electron
  • Full Text Search

以上只列出部分示例,瞭解更多示例請訪問:Dexie.js - Sampleshttps://dexie.org/docs/Samples)。最後我們來簡單介紹一下各種 Web 儲存方案。

三、各種 Web 儲存方案簡介

3.1 Cookie

HTTP Cookie(也叫 Web Cookie 或瀏覽器 Cookie)是伺服器傳送到使用者瀏覽器並儲存在本地的一小塊資料,它會在瀏覽器下次向同一伺服器再發起請求時被攜帶併傳送到伺服器上。通常,它用於告知服務端兩個請求是否來自同一瀏覽器,如保持使用者的登入狀態。

Cookie 主要用於以下三個方面:

  • 會話狀態管理(如使用者登入狀態、購物車、遊戲分數或其它需要記錄的資訊);
  • 個性化設定(如使用者自定義設定、主題等);
  • 瀏覽器行為跟蹤(如跟蹤分析使用者行為等)。

Cookie 的特點:

  • Cookie 的大小受限,一般為 4 KB;
  • 同一個域名下存放 Cookie 的個數是有限制的,不同瀏覽器的個數不一樣,一般為 20 個;
  • Cookie 支援設定過期時間,當過期時自動銷燬;
  • 每次發起同域下的 HTTP 請求時,都會攜帶當前域名下的 Cookie;
  • 支援設定為 HttpOnly,防止 Cookie 被客戶端的 JavaScript 訪問。

示例1:簡單用法

document.cookie = "name=semlinker";
document.cookie = "favorite_food=tripe";

alert(document.cookie);
// 顯示: name=semlinker;favorite_food=tripe

示例2:得到名為 test2 的 cookie

document.cookie = "test1=Hello";
document.cookie = "test2=World";

var myCookie = document.cookie
    .replace(/(?:(?:^|.*;\s*)test2\s*\=\s*([^;]*).*$)|^.*$/, "$1");
alert(myCookie);

3.2 localStorage

一種持久化的儲存方式,也就是說如果不手動清除,資料就永遠不會過期。它是採用鍵值對的方式儲存資料,按域名將資料分別儲存到對應資料庫檔案裡。相比 Cookie 來說,它能儲存更大的資料。

localStorage 的特點:

  • 大小限制為 5MB ~10MB;
  • 在同源的所有標籤頁和視窗之間共享資料;
  • 資料僅儲存在客戶端,不與伺服器進行通訊;
  • 資料持久存在且不會過期,重啟瀏覽器後仍然存在;
  • 對資料的操作是同步的。

示例

// 通過setItem()增加一個資料項
localStorage.setItem('myName', 'Semlinker');

// 通過getItem()獲取某個資料項
let me = localStorage.getItem('myName');

// 通過removeItem()移除某個資料項
localStorage.removeItem('myName');

// 移除所有資料項
localStorage.clear();

3.3 sessionStorage

與服務端的 session 類似,sessionStorage 是一種會話級別的快取,關閉瀏覽器時資料會被清除。需要注意的是 sessionStorage 的作用域是視窗級別的,也就是說不同視窗之間儲存的 sessionStorage 資料是不能共享的。

sessionStorage 的特點:

  • sessionStorage 的資料只存在於當前瀏覽器的標籤頁;
  • 資料在頁面重新整理後依然存在,但在關閉瀏覽器標籤頁之後資料就會被清除;
  • 與 localStorage 擁有統一的 API 介面;
  • 對資料的操作是同步的。

示例

// 通過setItem()增加一個資料項
sessionStorage.setItem('myName', 'Semlinker');

// 通過getItem()獲取某個資料項
let me = sessionStorage.getItem('myName');

// 通過removeItem()移除某個資料項
sessionStorage.removeItem('myName');

// 移除所有資料項
sessionStorage.clear();

3.4 Web SQL

Web SQL 資料庫 API 實際上不是 HTML5 規範的一部分,而是一個單獨的規範,它引入了一組 API 來使用 SQL 來操作客戶端資料庫。需要注意的是,HTML5 已經放棄 Web SQL 資料庫。

Web SQL Database 規範中定義的三個核心方法:

  • openDatabase:這個方法使用現有資料庫或新建資料庫來建立資料庫物件;
  • transaction:這個方法允許我們根據情況控制事務的提交或回滾;
  • executeSql:這個方法用於執行真實的 SQL 語句。

Web SQL 的特點(相比 Cookie、localStorage 與 sessionStorage):

  • Web SQL 能方便進行物件儲存;
  • Web SQL 支援事務,能方便地進行資料查詢和資料處理操作。

示例

var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);

db.transaction(function (tx) { 
   // 執行查詢操作
   tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)'); 
   // 執行插入操作
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")'); 
   tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")'); 
}); 

3.5 IndexedDB

IndexedDB 是一種底層 API,用於客戶端儲存大量結構化資料,包括檔案、二進位制大型物件。該 API 使用索引來實現對該資料的高效能搜尋。雖然 Web Storage 對於儲存較少量的資料很有用,但對於儲存更大量的結構化資料來說,這種方法不太好用。IndexedDB 提供了一個解決方案。

IndexedDB 的特點:

  • 儲存空間大:儲存空間可以達到幾百兆甚至更多;
  • 支援二進位制儲存:它不僅可以儲存字串,而且還可以儲存二進位制資料;
  • IndexedDB 有同源限制,每一個資料庫只能在自身域名下能訪問,不能跨域名訪問;
  • 支援事務型:IndexedDB 執行的操作會按照事務來分組的,在一個事務中,要麼所有的操作都成功,要麼所有的操作都失敗;
  • 鍵值對儲存:IndexedDB 內部採用物件倉庫(object store)存放資料。所有型別的資料都可以直接存入,包括 JavaScript 物件。物件倉庫中,資料以 “鍵值對” 的形式儲存,每一個資料記錄都有對應的主鍵,主鍵是獨一無二的,不能有重複,否則會丟擲一個錯誤。
  • 資料操作是非同步的:使用 IndexedDB 執行的操作是非同步執行的,以免阻塞應用程式。

示例

var dbName = "my_db";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // 錯誤處理
};

request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // 建立一個物件倉庫來儲存我們客戶的相關資訊,我們選擇 ssn 作為鍵路徑(key path)
  // 因為 ssn 可以保證是不重複的
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // 建立一個索引來通過姓名來搜尋客戶。名字可能會重複,所以我們不能使用 unique 索引
  objectStore.createIndex("name", "name", { unique: false });

  // 使用郵箱建立索引,我們確保客戶的郵箱不會重複,所以我們使用 unique 索引
  objectStore.createIndex("email", "email", { unique: true });

  // 使用事務的 oncomplete 事件確保在插入資料前物件倉庫已經建立完畢
  objectStore.transaction.oncomplete = function(event) {
    // 將資料儲存到新建立的物件倉庫
    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
    customerData.forEach(function(customer) {
      customerObjectStore.add(customer);
    });
  };
};

篇幅有限這裡我們只介紹了部分開源庫,其實還有一些其它成熟的開源庫,比如 lowdb(Local JSON Database)、Lovefield(Relational Database)和 LokiJS(NoSQL Database)等,如果你知道其它好玩的專案,歡迎給阿寶哥留言喲。

四、參考資源

推薦閱讀