前端的数据存储方式,你除了用过 Cookies、localStorage 和 sessionStorage 外,还有用过其它的存储方式么?其实除了前面提到的 3 种存储方式,目前主流的浏览器还支持 Web SQL 和 IndexedDB。
目前市场上主流的浏览器有 Chrome 、Safari、Firefox、Opera、UC Browser 和 Internet Explorer 等,其中截止 2020 年 5 月,Chrome 的市场占有率为 「63.93%」,远远超过第二名 Safari 浏览(「18.19%」)。
(图片来源:https://gs.statcounter.com/)
这里我们以市场占有率第一的 Chrome 浏览器为例,来了解一下它支持的所有存储方案:
(打开 Chrome 开发者工具,切换至 Application 栏位)
在介绍目前比较流行的一些开源的前端存储方案之前,阿宝哥先分享一些与存储有关,有趣好玩的开源库。
❝Realtime database backend based on Operational Transformation (OT)。 https://github.com/share/sharedb ❞
ShareDB 是一个基于 JSON 文档操作转换(OT)的实时数据库后端。它是 DerbyJS Web 应用程序框架的实时后端。
「示例1:实时数据同步」
「示例2:展示实时查询的排行榜应用程序」
❝? A relentless key-value store for the browser。 https://github.com/gruns/ImmortalDB ❞
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
❝对 localStorage 和 sessionStorage 进行了扩展,添加了超时时间,序列化方法。 https://github.com/wuchangming/web-storage-cache ❞
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'});
❝LZ-based compression algorithm for JavaScript。 https://github.com/pieroxy/lz-string/ ❞
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);
下图是使用官方在线示例进行字符串压缩测试的结果:
(图片来源:https://pieroxy.net/blog/pages/lz-string/demo.html)
接下来我们开始来介绍一些主流的数据库。
❝? 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)),进而改善你的 Web 应用程序的离线体验。
对于不支持 IndexedDB 或 WebSQL 的浏览器,localForage 会使用 localStorage 进行数据存储。此外,localForage 还支持存储所有可以序列化为 JSON 的原生 JS 对象以及 ArrayBuffers,Blob 和 TypedArrays。
localForage 主要支持的平台:
❝? - PouchDB is a pocket-sized database. https://github.com/pouchdb/pouchdb ❞
PouchDB 是一个浏览器内数据库,允许应用程序在本地保存数据,以便用户即使在离线时也可以享受应用程序的所有功能。另外,数据在客户端之间是同步的,因此用户可以随时随地保持最新状态。
PouchDB 也在 Node.js 中运行,可以用作与 「CouchDB」 兼容的服务器的直接接口。该 API 在每个环境中工作都是相同的,因此你可以花更少的时间来担心浏览器的差异,而花更多的时间来编写干净、一致的代码。
PouchDB 支持所有现代浏览器:
PouchDB 在幕后使用 IndexedDB,若当前环境不支持 IndexedDB 则回退到 Web SQL。
❝? ? ? A realtime Database for JavaScript Applications. https://github.com/pubkey/rxdb ❞
RxDB(Reactive Database 的缩写)是 NoSQL 数据库,用于 JavaScript 应用程序,如网站,混合应用程序,Electron Apps,Progressive Web Apps 和 Node.js。响应式意味着你不仅可以查询当前状态,还可以订阅所有状态更改,比如查询的结果或文档的单个字段。
这对于基于 UI 的实时应用程序非常有用,因为它易于开发,并且具有很大的性能优势。为了在客户端和服务器之间复制数据,RxDB 提供了用于与任何 CouchDB 兼容端点以及自定义 GraphQL 端点进行实时复制的模块。
RxDB 支持以下特性:
❝The JavaScript Database, for Node.js, nw.js, electron and the browser. https://github.com/louischatriot/nedb ❞
NeDB 是一个 JavaScript 数据库,能够运行在 Node.js、nw.js、Electron 和浏览器环境。它是使用纯的 JavaScript 实现,不依赖其它库,提供的 API 是 MongoDB API 的子集,重要的是它的速度非常快:
ops (operation per second) 即表示每秒操作的次数。
❝A Minimalistic Wrapper for IndexedDB. https://github.com/dfahlander/Dexie.js ❞
Dexie.js 是 IndexedDB 的包装库,它提供了一套经过精心设计的 API,强大的错误处理,较强的可扩展性,此外它能够跟踪数据变化,支持 KeyRange (搜索不区分大小写,可设置匹方式和 OR 操作)。
Dexie.js 主要为了解决原生 IndexedDB API 中存在的三个主要问题:
为了便于开发者接入 Dexie.js,在 Dexie.js 官网中提供了丰富的示例:
以上只列出部分示例,了解更多示例请访问:Dexie.js - Samples(https://dexie.org/docs/Samples)。最后我们来简单介绍一下各种 Web 存储方案。
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。
Cookie 主要用于以下三个方面:
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);
一种持久化的存储方式,也就是说如果不手动清除,数据就永远不会过期。它是采用键值对的方式存储数据,按域名将数据分别保存到对应数据库文件里。相比 Cookie 来说,它能保存更大的数据。
localStorage 的特点:
「示例」
// 通过setItem()增加一个数据项
localStorage.setItem('myName', 'Semlinker');
// 通过getItem()获取某个数据项
let me = localStorage.getItem('myName');
// 通过removeItem()移除某个数据项
localStorage.removeItem('myName');
// 移除所有数据项
localStorage.clear();
与服务端的 session 类似,sessionStorage 是一种会话级别的缓存,关闭浏览器时数据会被清除。需要注意的是 sessionStorage 的作用域是窗口级别的,也就是说不同窗口之间保存的 sessionStorage 数据是不能共享的。
sessionStorage 的特点:
「示例」
// 通过setItem()增加一个数据项
sessionStorage.setItem('myName', 'Semlinker');
// 通过getItem()获取某个数据项
let me = sessionStorage.getItem('myName');
// 通过removeItem()移除某个数据项
sessionStorage.removeItem('myName');
// 移除所有数据项
sessionStorage.clear();
Web SQL 数据库 API 实际上不是 HTML5 规范的一部分,而是一个单独的规范,它引入了一组 API 来使用 SQL 来操作客户端数据库。需要注意的是,HTML5 已经放弃 Web SQL 数据库。
Web SQL Database 规范中定义的三个核心方法:
Web SQL 的特点(相比 Cookie、localStorage 与 sessionStorage):
「示例」
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")');
});
IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件、二进制大型对象。该 API 使用索引来实现对该数据的高性能搜索。虽然 Web Storage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太好用。IndexedDB 提供了一个解决方案。
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)等,如果你知道其它好玩的项目,欢迎给阿宝哥留言哟。