前端的數(shù)據(jù)存儲方式,你除了用過 Cookies、localStorage 和 sessionStorage 外,還有用過其它的存儲方式么?其實除了前面提到的 3 種存儲方式,目前主流的瀏覽器還支持 Web SQL 和 IndexedDB。
目前市場上主流的瀏覽器有 Chrome 、Safari、Firefox、Opera、UC Browser 和 Internet Explorer 等,其中截止 2020 年 5 月,Chrome 的市場占有率為 「63.93%」,遠(yuǎn)遠(yuǎn)超過第二名 Safari 瀏覽(「18.19%」)。
(圖片來源:https://gs.statcounter.com/)
這里我們以市場占有率第一的 Chrome 瀏覽器為例,來了解一下它支持的所有存儲方案:
(打開 Chrome 開發(fā)者工具,切換至 Application 欄位)
在介紹目前比較流行的一些開源的前端存儲方案之前,阿寶哥先分享一些與存儲有關(guān),有趣好玩的開源庫。
?Realtime database backend based on Operational Transformation (OT)。
https://github.com/share/sharedb
?
ShareDB 是一個基于 JSON 文檔操作轉(zhuǎn)換(OT)的實時數(shù)據(jù)庫后端。它是 DerbyJS Web 應(yīng)用程序框架的實時后端。
「示例1:實時數(shù)據(jù)同步」
「示例2:展示實時查詢的排行榜應(yīng)用程序」
??? A relentless key-value store for the browser。
https://github.com/gruns/ImmortalDB
?
ImmortalDB 是在瀏覽器中存儲持久鍵值數(shù)據(jù)的最佳方法。保存到 ImmortalDB 的數(shù)據(jù)被冗余地存儲在 Cookies,IndexedDB 和 localStorage 中,并且如果其中的任何數(shù)據(jù)被刪除或損壞,它們將不斷進(jìn)行自我修復(fù)。
例如,清除 Cookie 是一種常見的用戶操作,即使對于非技術(shù)用戶也是如此。在存儲壓力下,瀏覽器在沒有警告的情況下隨意刪除 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
?對 localStorage 和 sessionStorage 進(jìn)行了擴(kuò)展,添加了超時時間,序列化方法。
https://github.com/wuchangming/web-storage-cache
?
WebStorageCache 對 HTML5 localStorage 和 sessionStorage 進(jìn)行了擴(kuò)展,「添加了超時時間,序列化方法」??梢灾苯哟鎯?JSON 對象,同時可以非常簡單的進(jìn)行超時時間的設(shè)置。
?優(yōu)化:WebStorageCache 自動清除訪問的過期數(shù)據(jù),避免了過期數(shù)據(jù)的累積。另外也提供了清除全部過期數(shù)據(jù)的方法: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對象,默認(rèn)使用序列化方法為JSON.stringify。
// 可以通過初始化wsCache的時候配置serializer.serialize
wsCache.set('user', { name: 'Wu', organization: 'wqteam'});
?LZ-based compression algorithm for JavaScript。
https://github.com/pieroxy/lz-string/
?
lz-string 旨在滿足在 localStorage 中(尤其是在移動設(shè)備上)存儲大量數(shù)據(jù)的需求。localStorage 通常限制為 5MB ~10MB,你可以通過對數(shù)據(jù)進(jìn)行壓縮,以存儲更多的數(shù)據(jù)。
「示例」
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);
下圖是使用官方在線示例進(jìn)行字符串壓縮測試的結(jié)果:
(圖片來源:https://pieroxy.net/blog/pages/lz-string/demo.html)
接下來我們開始來介紹一些主流的數(shù)據(jù)庫。
??? Offline storage, improved. Wraps IndexedDB, WebSQL, or localStorage using a simple but powerful API.
https://github.com/localForage/localForage
?
localForage 是一個快速簡單的 JavaScript 存儲庫。它通過使用類似于 localStorage 的簡單 API 來使用異步存儲(IndexedDB 或 WebSQL)),進(jìn)而改善你的 Web 應(yīng)用程序的離線體驗。
對于不支持 IndexedDB 或 WebSQL 的瀏覽器,localForage 會使用 localStorage 進(jìn)行數(shù)據(jù)存儲。此外,localForage 還支持存儲所有可以序列化為 JSON 的原生 JS 對象以及 ArrayBuffers,Blob 和 TypedArrays。
localForage 主要支持的平臺:
??? - PouchDB is a pocket-sized database.
https://github.com/pouchdb/pouchdb
?
PouchDB 是一個瀏覽器內(nèi)數(shù)據(jù)庫,允許應(yīng)用程序在本地保存數(shù)據(jù),以便用戶即使在離線時也可以享受應(yīng)用程序的所有功能。另外,數(shù)據(jù)在客戶端之間是同步的,因此用戶可以隨時隨地保持最新狀態(tài)。
PouchDB 也在 Node.js 中運(yùn)行,可以用作與 「CouchDB」 兼容的服務(wù)器的直接接口。該 API 在每個環(huán)境中工作都是相同的,因此你可以花更少的時間來擔(dān)心瀏覽器的差異,而花更多的時間來編寫干凈、一致的代碼。
PouchDB 支持所有現(xiàn)代瀏覽器:
PouchDB 在幕后使用 IndexedDB,若當(dāng)前環(huán)境不支持 IndexedDB 則回退到 Web SQL。
??? ?? ?? A realtime Database for JavaScript Applications.
https://github.com/pubkey/rxdb
?
RxDB(Reactive Database 的縮寫)是 NoSQL 數(shù)據(jù)庫,用于 JavaScript 應(yīng)用程序,如網(wǎng)站,混合應(yīng)用程序,Electron Apps,Progressive Web Apps 和 Node.js。響應(yīng)式意味著你不僅可以查詢當(dāng)前狀態(tài),還可以訂閱所有狀態(tài)更改,比如查詢的結(jié)果或文檔的單個字段。
這對于基于 UI 的實時應(yīng)用程序非常有用,因為它易于開發(fā),并且具有很大的性能優(yōu)勢。為了在客戶端和服務(wù)器之間復(fù)制數(shù)據(jù),RxDB 提供了用于與任何 CouchDB 兼容端點(diǎn)以及自定義 GraphQL 端點(diǎn)進(jìn)行實時復(fù)制的模塊。
RxDB 支持以下特性:
?The JavaScript Database, for Node.js, nw.js, electron and the browser.
https://github.com/louischatriot/nedb
?
NeDB 是一個 JavaScript 數(shù)據(jù)庫,能夠運(yùn)行在 Node.js、nw.js、Electron 和瀏覽器環(huán)境。它是使用純的 JavaScript 實現(xiàn),不依賴其它庫,提供的 API 是 MongoDB API 的子集,重要的是它的速度非常快:
插入:10,680 ops/s
查找:43,290 ops/s
更新:8,000 ops/s。
刪除:11,750 ops/s。
ops (operation per second) 即表示每秒操作的次數(shù)。
?A Minimalistic Wrapper for IndexedDB.
https://github.com/dfahlander/Dexie.js
?
Dexie.js 是 IndexedDB 的包裝庫,它提供了一套經(jīng)過精心設(shè)計的 API,強(qiáng)大的錯誤處理,較強(qiáng)的可擴(kuò)展性,此外它能夠跟蹤數(shù)據(jù)變化,支持 KeyRange (搜索不區(qū)分大小寫,可設(shè)置匹方式和 OR 操作)。
Dexie.js 主要為了解決原生 IndexedDB API 中存在的三個主要問題:
為了便于開發(fā)者接入 Dexie.js,在 Dexie.js 官網(wǎng)中提供了豐富的示例:
以上只列出部分示例,了解更多示例請訪問:Dexie.js - Samples(https://dexie.org/docs/Samples)。最后我們來簡單介紹一下各種 Web 存儲方案。
HTTP Cookie(也叫 Web Cookie 或瀏覽器 Cookie)是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),它會在瀏覽器下次向同一服務(wù)器再發(fā)起請求時被攜帶并發(fā)送到服務(wù)器上。通常,它用于告知服務(wù)端兩個請求是否來自同一瀏覽器,如保持用戶的登錄狀態(tài)。
Cookie 主要用于以下三個方面:
Cookie 的特點(diǎn):
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);
一種持久化的存儲方式,也就是說如果不手動清除,數(shù)據(jù)就永遠(yuǎn)不會過期。它是采用鍵值對的方式存儲數(shù)據(jù),按域名將數(shù)據(jù)分別保存到對應(yīng)數(shù)據(jù)庫文件里。相比 Cookie 來說,它能保存更大的數(shù)據(jù)。
localStorage 的特點(diǎn):
「示例」
// 通過setItem()增加一個數(shù)據(jù)項
localStorage.setItem('myName', 'Semlinker');
// 通過getItem()獲取某個數(shù)據(jù)項
let me = localStorage.getItem('myName');
// 通過removeItem()移除某個數(shù)據(jù)項
localStorage.removeItem('myName');
// 移除所有數(shù)據(jù)項
localStorage.clear();
與服務(wù)端的 session 類似,sessionStorage 是一種會話級別的緩存,關(guān)閉瀏覽器時數(shù)據(jù)會被清除。需要注意的是 sessionStorage 的作用域是窗口級別的,也就是說不同窗口之間保存的 sessionStorage 數(shù)據(jù)是不能共享的。
sessionStorage 的特點(diǎn):
「示例」
// 通過setItem()增加一個數(shù)據(jù)項
sessionStorage.setItem('myName', 'Semlinker');
// 通過getItem()獲取某個數(shù)據(jù)項
let me = sessionStorage.getItem('myName');
// 通過removeItem()移除某個數(shù)據(jù)項
sessionStorage.removeItem('myName');
// 移除所有數(shù)據(jù)項
sessionStorage.clear();
Web SQL 數(shù)據(jù)庫 API 實際上不是 HTML5 規(guī)范的一部分,而是一個單獨(dú)的規(guī)范,它引入了一組 API 來使用 SQL 來操作客戶端數(shù)據(jù)庫。需要注意的是,HTML5 已經(jīng)放棄 Web SQL 數(shù)據(jù)庫。
Web SQL Database 規(guī)范中定義的三個核心方法:
openDatabase:這個方法使用現(xiàn)有數(shù)據(jù)庫或新建數(shù)據(jù)庫來創(chuàng)建數(shù)據(jù)庫對象;
transaction:這個方法允許我們根據(jù)情況控制事務(wù)的提交或回滾;
executeSql:這個方法用于執(zhí)行真實的 SQL 語句。
Web SQL 的特點(diǎn)(相比 Cookie、localStorage 與 sessionStorage):
「示例」
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
// 執(zhí)行查詢操作
tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)');
// 執(zhí)行插入操作
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, 'foobar')');
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, 'logmsg')');
});
IndexedDB 是一種底層 API,用于客戶端存儲大量結(jié)構(gòu)化數(shù)據(jù),包括文件、二進(jìn)制大型對象。該 API 使用索引來實現(xiàn)對該數(shù)據(jù)的高性能搜索。雖然 Web Storage 對于存儲較少量的數(shù)據(jù)很有用,但對于存儲更大量的結(jié)構(gòu)化數(shù)據(jù)來說,這種方法不太好用。IndexedDB 提供了一個解決方案。
IndexedDB 的特點(diǎn):
「示例」
var dbName = 'my_db';
var request = indexedDB.open(dbName, 2);
request.onerror = function(event) {
// 錯誤處理
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
// 建立一個對象倉庫來存儲我們客戶的相關(guān)信息,我們選擇 ssn 作為鍵路徑(key path)
// 因為 ssn 可以保證是不重復(fù)的
var objectStore = db.createObjectStore('customers', { keyPath: 'ssn' });
// 建立一個索引來通過姓名來搜索客戶。名字可能會重復(fù),所以我們不能使用 unique 索引
objectStore.createIndex('name', 'name', { unique: false });
// 使用郵箱建立索引,我們確保客戶的郵箱不會重復(fù),所以我們使用 unique 索引
objectStore.createIndex('email', 'email', { unique: true });
// 使用事務(wù)的 oncomplete 事件確保在插入數(shù)據(jù)前對象倉庫已經(jīng)創(chuàng)建完畢
objectStore.transaction.oncomplete = function(event) {
// 將數(shù)據(jù)保存到新創(chuàng)建的對象倉庫
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)等,如果你知道其它好玩的項目,歡迎給阿寶哥留言喲。
聯(lián)系客服