这是MDN原文对IndexedDB的一种描述:
IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。
IndexedDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。然而,不像 RDBMS 使用固定列表,IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许您存储和检索用键索引的对象;可以存储结构化克隆算法支持的任何对象。您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务。
developer.mozilla.org/zh-CN/docs/…
特性
- 仅支持浏览器客户端
- 支持异步、同步(最新规范已不推荐)两种Api
- 数据库绑定域名
- 基于Web Worker的线程运行
- 具备数据库的基本特征
- 存储空间上限为浏览器的最高配额
浏览器的最大存储空间是动态的——它取决于您的硬盘大小。 全局限制为可用磁盘空间的 50%。在 Firefox 中,一个名为 Quota Manager 的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。因此,如果您的硬盘驱动器是 500GB,那么浏览器的总存储容量为 250GB。如果超过此范围,则会发起称为源回收的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。
花式变脸
本文不探讨 IndexedDB 的标准 API,我们主要了解一下基于 IndexedDB 为基础,这些年技术开发者通过包装实现的方式,产生了一些优秀的工具库。
包括 localForage、dexie.js、PouchDB、idb、idb-keyval、JsStore 或者 lovefield 等,当开发者使用这些库时,会很自然的感受到其友好、强大的一面,不仅仅实现了简洁的键值对接口,更令人惊叹是部分包装器支持了类似 NoSQL 和 SQL语法 的操作形式,无论您习惯于哪种数据库的操作方式,都可以顺利上手。
为了启发思考,我们重点介绍其中三款具有代表性的 IndexedDB 封装库。
1. localForage
一个简单的 Polyfill,提供了简单的客户端数据存储的值语法,基于key-value的形式来操作存储。localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。在所有主流浏览器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。
localforage.config({
driver : localforage.INDEXEDDB, // Force IndexedDB; same as using setDriver()
name : 'mydb',
version : 1.0,
storeName : 'keyvaluepairs', // Should be alphanumeric, with underscores.
description : 'some description'
});
localforage.setItem('key', 'value', function (err) {
// if err is non-null, we got an error
localforage.getItem('key', function (err, value) {
// if err is non-null, we got an error. otherwise, value is the value
});
});
2. ZangoDB
ZangoDB是IndexedDB的一个类似MongoDB的接口,支持MongoDB的大多数常见过滤、投影、排序、更新和聚合功能。
let db = new zango.Db('mydb', { people: ['age'] });
let people = db.collection('people');
let docs = [
{ name: 'Frank', age: 20 },
{ name: 'Thomas', age: 33 },
{ name: 'Todd', age: 33 },
{ name: 'John', age: 28 },
{ name: 'Peter', age: 33 },
{ name: 'George', age: 28 }
];
people.insert(docs).then(() => {
return people.find({
name: { $ne: 'John' },
age: { $gt: 20 }
}).group({
_id: { age: '$age' },
count: { $sum: 1 }
}).project({
_id: 0,
age: '$_id.age'
}).sort({
age: -1
}).forEach(doc => console.log('doc:', doc));
}).catch(error => console.error(error));
//out put
doc: { count: 3, age: 33 }
doc: { count: 1, age: 28 }
3. JsStore
JsStore 是一个 IndexedDB 包装器。它通过类似 SQL 的 api 让 IndexedDB 变得超级简单,其数据库模式是一个包含数据库名称和表列表的对象。
// initiate jsstore connection
var connection = new JsStore.Connection();
// step1 - create database schema
var tblProduct = {
name: 'Product',
columns: {
// Here "Id" is name of column
Id:{ primaryKey: true, autoIncrement: true },
ItemName: { notNull: true, dataType: "string" },
Price: { notNull: true, dataType: "number" },
Quantity : { notNull: true, dataType: "number" }
}
};
var tblOrder = {
name: 'Order',
columns: {
// Here "OrderId" is name of column
OrderId:{ primaryKey: true, autoIncrement: true }
}
};
var db = {
name: dbName,
tables: [tblProduct, tblOrder]
}
// step 2
var isDbCreated = await connection.initDb(db);
// isDbCreated will be true when database will be initiated for first time
if(isDbCreated){
alert('Db Created & connection is opened');
}
else{
alert('Connection is opened');
}
// since connection is opened now, you can call apis.
Copy
var value = {
column1: value1,
column2: value2,
column3: value3,
...
columnN: valueN
};
var noOfRowsInserted = await connection.insert({
into: "TABLE_NAME",
values: [Value], //you can insert multiple values at a time
});
if (noOfRowsInserted > 0) {
alert('Successfully Added');
}
Copy
var results = await connection.select({
from: table1 name,
where: {
[column name]: some value
},
join: {
with: table2_name,
on: "table1.common_field=table2.common_field",
type:"inner",
where: {
[column name]: some value
}
}
});
console.log(results);
PS:IndexedDB 的包装器,如同裁缝为不同顾客定制的服装。面对复杂的应用程序,我们可以根据开发者的工作习惯和需求,定制出不同形式的API,就像裁缝为不同体型的客户量身定做合身的衣物。这种“量体裁衣”的设计思想,可以让开发者获得更好的使用体验,提高工作效率。代码世界日新月异,但优秀的工具向来本于用户,立足实用,始终不忘初心。