# 浏览器存储-IndexedDB
IndexedDB 是一个浏览器内置的数据库,它比 localStorage 强大得多。
- 通过支持多种类型的键,来存储几乎可以是任何类型的值。
- 支撑事务的可靠性。
- 支持键范围查询、索引。
- 和 localStorage 相比,它可以存储更大的数据量。
对于传统的 客户端-服务器 应用,这些功能通常是没有必要的。IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。
# 打开数据库
要想使用 IndexedDB,首先需要 open(连接)一个数据库。
let openRequest = indexedDB.open(name, version);
- name —— 字符串,即数据库名称。
- version —— 一个正整数版本,默认为 1,用于版本控制 s
调用之后会返回 openRequest 对象,我们需要监听该对象上的事件:
- success:数据库准备就绪,openRequest.result 中有了一个数据库对象“Database Object”,使用它进行进一步的调用。
- error:打开失败。
- upgradeneeded:数据库已准备就绪,但其版本已过时(见下文)。
IndexedDB 具有内建的“模式(scheme)版本控制”机制,这在服务器端数据库中是不存在的。
与服务器端数据库不同,IndexedDB 存在于客户端,数据存储在浏览器中。因此,开发人员无法随时都能访问它。因此,当我们发布了新版本的应用程序,用户访问我们的网页,我们可能需要更新该数据库。
如果本地数据库版本低于 open 中指定的版本,会触发一个特殊事件 upgradeneeded。我们可以根据需要比较版本并升级数据结构。
当数据库还不存在时(从技术上讲,该版本为 0),也会触发 upgradeneeded 事件。因此,我们可以执行初始化。
# 版本控制
假设我们发布了应用程序的第一个版本。
let openRequest = indexedDB.open("store", 1);
openRequest.onupgradeneeded = function () {
// 如果客户端没有数据库则触发
// ...执行初始化...
};
openRequest.onsuccess = function () {
let db = openRequest.result;
// 继续使用 db 对象处理数据库
};
之后不久,我们发布了第二个版本。
let openRequest = indexedDB.open("store", 2);
// 现有的数据库版本小于 2(或不存在)
openRequest.onupgradeneeded = function (event) {
let db = openRequest.result;
switch (
event.oldVersion // 现有的 db 版本
) {
case 0:
// 版本 0 表示客户端没有数据库
// 执行初始化
case 1:
// 客户端版本为 1
// 更新
}
};
openRequest.onsuccess = function () {
let db = openRequest.result;
// 继续使用 db 对象处理数据库 此时数据库为版本2
};
删除数据库
let deleteRequest = indexedDB.deleteDatabase(name);
// 然后可以使用deleteRequest.onsuccess/onerror 追踪(tracks)结果
# 对象库
要在 IndexedDB 中存储某些内容,我们需要一个 对象库。
对象库是 IndexedDB 的核心概念,在其他数据库中对应的对象称为“表”或“集合”。
IndexedDB 使用 标准序列化算法 来克隆和存储对象。类似于 JSON.stringify,不过功能更加强大,能够存储更多的数据类型。
有一种对象不能被存储:循环引用的对象。此类对象不可序列化,也不能进行 JSON.stringify。
库中的每个值都必须有唯一的键 key。
db.createObjectStore(name[, keyOptions]);
- name 是存储区名称,例如 "books" 表示书。
- keyOptions 是具有以下两个属性之一的可选对象
如果我们不提供 keyOptions,那么以后需要在存储对象时,显式地提供一个键。
db.createObjectStore("books", { keyPath: "id" });
删除对象库:
db.deleteObjectStore("books");
# 事务
术语“事务”是通用的,许多数据库中都有用到。
事务是一组操作,要么全部成功,要么全部失败。
例如,当一个人买东西时,我们需要:
从他们的账户中扣除这笔钱。 将该项目添加到他们的清单中。 如果完成了第一个操作,但是出了问题,比如停电。这时无法完成第二个操作,这非常糟糕。两件时应该要么都成功(购买完成,好!)或同时失败(这个人保留了钱,可以重新尝试)。
事务可以保证同时完成。
启动事务:
db.transaction(store[, type]);
- store 是事务要访问的库名称,例如 "books"。如果我们要访问多个库,则是库名称的数组。
- type – 事务类型,分为 readonly 和 readwrite,其中前者性能更高,允许多个事务同时访问同一存储区
创建事务后,我们可以将项目添加到库,就像这样:
let transaction = db.transaction("books", "readwrite"); // (1)
// 通过事务获取对象库进行操作
let books = transaction.objectStore("books"); // (2)
let book = {
id: "js advanced",
price: 10,
created: new Date()
};
let request = books.add(book); // (3)
request.onsuccess = function () {
// (4)
console.log("Book added to the store", request.result);
};
request.onerror = function () {
console.log("Error", request.error);
};
上面我们只监听的一个数据操作,为了检测事务成功完成,们可以监听 transaction.oncomplete 事件:
let transaction = db.transaction("books", "readwrite");
// ……执行操作……
transaction.oncomplete = function () {
console.log("Transaction is complete"); // 事务执行完成
};