HTML5 Web IndexedDB API 是一种在用户浏览器中存储大量结构化数据的机制,它允许存储和检索键值对,其中键可以是任何有效的JavaScript对象。IndexedDB 主要用于需要复杂查询的数据密集型Web应用。
IndexedDB 的特点:
HTML5 Web IndexedDB API 是一种强大的客户端存储解决方案,它允许Web应用程序在用户的浏览器中存储大量的结构化数据。IndexedDB 的特点如下:
-
异步操作:
- 所有的数据库操作都是异步的,这意味着它们不会阻塞用户界面或其他脚本执行。这有助于保持网页的响应性。
-
键值对存储:
- 数据以对象的形式存储,每个对象与一个主键相关联。主键可以是任何有效的JavaScript对象(如字符串、数字等),也可以是由开发者定义的自动生成的ID。
-
事务支持:
- IndexedDB 使用事务来保证数据的一致性和完整性。事务提供了读取、写入和只读三种模式,并且确保了所有操作要么全部成功要么全部失败。
-
索引能力:
- 除了主键外,还可以为对象存储创建多个索引,以加速基于非主键字段的数据检索。这些索引使得复杂查询成为可能,比如查找特定范围内的记录或者按照某个属性排序。
-
版本控制系统:
- 每个数据库都有一个版本号,当需要修改数据库结构时(例如添加新的对象存储或索引),可以通过升级数据库版本来实现。
upgradeneeded
事件会在首次打开数据库或请求更高版本时触发,以便进行必要的更改。
- 每个数据库都有一个版本号,当需要修改数据库结构时(例如添加新的对象存储或索引),可以通过升级数据库版本来实现。
-
丰富的API:
- 提供了一系列的方法用于管理数据库、对象存储、索引以及执行CRUD(创建、读取、更新、删除)操作。API设计直观,易于使用。
-
大容量存储:
- IndexedDB 支持比本地存储(LocalStorage)更大的数据量,具体大小取决于用户设置和浏览器实现。理论上它可以存储数GB的数据。
-
持久化存储:
- 存储在 IndexedDB 中的数据是持久性的,除非被明确地删除或用户清空浏览器缓存,否则数据会一直保存。
-
跨域限制:
- IndexedDB 遵循同源策略(Same-origin policy),意味着只有来自相同源(协议、域名、端口)的页面才能访问同一个 IndexedDB 数据库。
-
事件驱动架构:
- IndexedDB 使用事件模型来进行错误处理和完成通知。例如,在尝试打开数据库或执行事务时发生的事件可以用来控制流程和响应结果。
通过这些特性,IndexedDB 成为了构建离线Web应用、增强用户体验和性能的一个重要工具。然而,由于其复杂性相对较高,对于简单的数据存储需求,可能会选择更简单的方案如 LocalStorage 或 SessionStorage。
基本使用步骤:
使用 HTML5 Web IndexedDB API 进行基本操作的步骤可以分为以下几个关键阶段:
1. 打开数据库
首先,你需要尝试打开一个已有的数据库或者创建一个新的数据库。这通过 indexedDB.open()
方法完成,该方法接受两个参数:数据库名称和版本号。
var request = indexedDB.open("MyDatabase", 1);
2. 处理升级需求(如果需要)
当首次创建数据库或数据库版本增加时,会触发 upgradeneeded
事件。在这个事件处理函数中,你可以定义数据库结构,包括创建对象存储空间和索引。
request.onupgradeneeded = function(event) {
var db = event.target.result;
// 创建一个名为 "contacts" 的对象存储空间,设置 "id" 作为主键
var objectStore = db.createObjectStore("contacts", { keyPath: "id", autoIncrement: true });
// 可选地,在对象存储上创建索引
objectStore.createIndex("nameIndex", "name", { unique: false });
};
3. 监听成功和错误事件
为了知道何时数据库已经成功打开以及是否有任何错误发生,你应该添加 onsuccess
和 onerror
事件监听器。
request.onsuccess = function(event) {
console.log("数据库打开成功");
db = event.target.result; // 保存对数据库的引用
};
request.onerror = function(event) {
console.error("数据库打开失败");
};
4. 开始事务
在进行任何数据操作之前,你必须开始一个事务。事务指定了要访问的对象存储空间列表,并声明了事务类型(只读 "readonly"
或读写 "readwrite"
)。
var transaction = db.transaction(["contacts"], "readwrite");
5. 获取对象存储
从事务中获取你要操作的对象存储空间。
var objectStore = transaction.objectStore("contacts");
6. 操作数据
根据你的需求,使用适当的方法来添加、获取、更新或删除数据。
- 添加/更新:使用
add()
或put()
方法。 - 获取:使用
get()
方法。 - 删除:使用
delete()
方法。 - 遍历:使用游标(cursor)遍历对象存储中的记录。
// 添加一条新记录到对象存储中
var addRequest = objectStore.add({ name: "张三", email: "zhangsan@example.com" });
addRequest.onsuccess = function(event) {
console.log("记录添加成功,ID:", event.target.result);
};
// 获取指定 ID 的记录
var getRequest = objectStore.get(1);
getRequest.onsuccess = function(event) {
if (event.target.result) {
console.log("找到记录:", event.target.result);
} else {
console.log("未找到记录");
}
};
7. 监听事务完成
监听事务的 complete
事件以确认所有操作都已完成。
transaction.oncomplete = function() {
console.log("事务已完成");
};
8. 关闭数据库连接
当你不再需要与数据库交互时,应该关闭数据库连接以释放资源。
db.close();
以上是使用 IndexedDB 的基本流程。根据实际应用的需求,可能还需要更复杂的逻辑和错误处理。记住,IndexedDB 是异步工作的,所以通常你会用回调函数、Promise 或者 async/await 来管理这些异步操作。
示例代码
以下是六个使用 HTML5 Web IndexedDB 的示例,每个示例都展示了不同的功能和用法:
示例 1: 创建数据库和对象存储
// 尝试打开名为 "MyDatabase" 的数据库,如果不存在则创建之,版本号为 1。
var request = indexedDB.open("MyDatabase", 1);
// 当需要升级数据库时触发此事件(首次创建或版本号增加)。
request.onupgradeneeded = function(event) {
// 获取数据库实例
var db = event.target.result;
// 创建一个名为 "books" 的对象存储空间,并指定 "isbn" 作为主键。
// 每个存储在 "books" 中的对象都必须有一个名为 "isbn" 的属性作为其唯一标识符。
var objectStore = db.createObjectStore("books", { keyPath: "isbn" });
// 可选地,在这里可以为 "books" 对象存储创建索引,以加速特定字段的查询。
// 例如:objectStore.createIndex("titleIndex", "title", { unique: false });
};
// 数据库成功打开时触发此事件。
request.onsuccess = function(event) {
// 保存对数据库连接的引用(在这个简单的例子中未使用)
var db = event.target.result;
// 输出确认信息到控制台,表示数据库已经成功打开。
console.log("数据库已成功打开");
// 注意:在此之后,你通常会开始进行事务操作(如添加、读取、更新或删除数据)。
};
// 如果打开数据库过程中出现错误,则触发此事件。
request.onerror = function(event) {
// 打印错误信息到控制台,帮助调试问题。
console.error("数据库打开失败");
};
示例 2: 向对象存储中添加记录
function addBook(db, book) {
// 开始一个新事务,指定要访问的对象存储空间("books"),并设置为读写模式 ("readwrite")。
var transaction = db.transaction(["books"], "readwrite");
// 从当前事务中获取名为 "books" 的对象存储空间实例。
var objectStore = transaction.objectStore("books");
// 向对象存储空间中添加一个新的书本记录。如果主键(这里是指定的 "isbn" 字段)已经存在,则会抛出错误。
var addRequest = objectStore.add(book);
// 当添加操作成功完成时触发此事件处理程序。
addRequest.onsuccess = function(event) {
// 输出确认信息到控制台,表示书本已成功添加。
console.log("书本添加成功");
};
// 当整个事务完成时触发此事件处理程序,无论是否有任何操作失败。
transaction.oncomplete = function() {
// 输出信息到控制台,表示所有事务内的操作已完成。
console.log("事务完成");
};
// 如果事务中的任何一个操作发生错误,则触发此事件处理程序。
transaction.onerror = function(event) {
// 输出错误信息到控制台,帮助调试问题。
console.error("添加失败");
};
}
// 使用方法:尝试向数据库中添加一本书。
// 注意:这里的 "db" 参数应该是由 indexedDB.open 成功返回的数据库连接对象,而不是字符串 "MyDatabase"。
// 正确的做法是先打开数据库,然后使用返回的数据库连接对象调用 addBook 函数。
var request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
// 获取数据库连接
var db = event.target.result;
// 现在可以安全地调用 addBook 函数,并传递实际的数据库连接对象。
addBook(db, { isbn: '123456789', title: 'HTML5入门' });
// addBook(db, { isbn: '12345', title: 'go入门' });
// 关闭数据库连接(通常在所有操作完成后进行)
// db.close();
};
request.onerror = function(event) {
console.error("数据库打开失败");
};
示例 3: 查询特定记录
function getBook(db, isbn) {
// 开始一个新事务,指定要访问的对象存储空间("books"),并设置为只读模式 ("readonly")。
// 由于我们只是查询数据而不会修改它,所以使用 "readonly" 是合适的。
var transaction = db.transaction(["books"], "readonly");
// 从当前事务中获取名为 "books" 的对象存储空间实例。
var objectStore = transaction.objectStore("books");
// 尝试根据提供的 ISBN 获取书本记录。
var getRequest = objectStore.get(isbn);
// 当获取操作成功完成时触发此事件处理程序。
getRequest.onsuccess = function(event) {
// 检查是否找到了匹配的书本记录。
if (event.target.result) {
// 如果找到,输出书本信息到控制台。
console.log("找到的书本:", event.target.result);
} else {
// 如果没有找到对应的 ISBN,输出未找到的信息。
console.log("未找到该ISBN的书本");
}
};
// 注意:对于只读事务,通常不需要监听 oncomplete 或 onerror 事件,
// 因为这些事件主要用于确保所有写入操作都已成功完成或用于错误处理。
}
// 使用方法:尝试从数据库中获取一本书。
// 注意:这里的 "db" 参数应该是由 indexedDB.open 成功返回的数据库连接对象,而不是字符串 "MyDatabase"。
// 正确的做法是先打开数据库,然后使用返回的数据库连接对象调用 getBook 函数。
var request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
// 获取数据库连接
var db = event.target.result;
// 现在可以安全地调用 getBook 函数,并传递实际的数据库连接对象。
getBook(db, '123456789');
// 关闭数据库连接(通常在所有操作完成后进行)
// db.close();
};
request.onerror = function(event) {
console.error("数据库打开失败");
};
示例 4: 更新现有记录
function updateBook(db, updatedBook) {
// 开始一个新事务,指定要访问的对象存储空间("books"),并设置为读写模式 ("readwrite")。
// 由于我们打算修改数据,因此需要使用 "readwrite" 模式以允许对数据库进行更改。
var transaction = db.transaction(["books"], "readwrite");
// 从当前事务中获取名为 "books" 的对象存储空间实例。
var objectStore = transaction.objectStore("books");
// 使用 put() 方法更新或插入书本记录。
// 如果对象存储中已存在具有相同主键(这里是指定的 "isbn" 字段)的记录,则该记录会被更新;
// 如果不存在这样的记录,则会创建一条新的记录。
var putRequest = objectStore.put(updatedBook);
// 当 put() 操作成功完成时触发此事件处理程序。
putRequest.onsuccess = function(event) {
// 输出确认信息到控制台,表示书本已成功更新。
console.log("书本更新成功");
};
// 当整个事务完成时触发此事件处理程序,无论是否有任何操作失败。
transaction.oncomplete = function() {
// 输出信息到控制台,表示所有事务内的操作已完成。
console.log("事务完成");
};
// 如果事务中的任何一个操作发生错误,则触发此事件处理程序。
transaction.onerror = function(event) {
// 输出错误信息到控制台,帮助调试问题。
console.error("更新失败");
};
}
// 使用方法:尝试更新数据库中的一本书。
// 注意:这里的 "db" 参数应该是由 indexedDB.open 成功返回的数据库连接对象,而不是字符串 "MyDatabase"。
// 正确的做法是先打开数据库,然后使用返回的数据库连接对象调用 updateBook 函数。
var request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
// 获取数据库连接
var db = event.target.result;
// 现在可以安全地调用 updateBook 函数,并传递实际的数据库连接对象。
updateBook(db, { isbn: '123456789', title: 'HTML5进阶' });
// 关闭数据库连接(通常在所有操作完成后进行)
// db.close();
};
request.onerror = function(event) {
console.error("数据库打开失败");
};
示例 5: 删除记录
function deleteBook(db, isbn) {
// 开始一个新事务,指定要访问的对象存储空间("books"),并设置为读写模式 ("readwrite")。
// 由于我们打算删除数据,因此需要使用 "readwrite" 模式以允许对数据库进行更改。
var transaction = db.transaction(["books"], "readwrite");
// 从当前事务中获取名为 "books" 的对象存储空间实例。
var objectStore = transaction.objectStore("books");
// 使用 delete() 方法尝试根据提供的 ISBN 删除书本记录。
// 如果对象存储中存在具有相同主键(这里是指定的 "isbn" 字段)的记录,则该记录会被删除;
// 如果不存在这样的记录,则不会有任何操作执行,但也不会抛出错误。
var deleteRequest = objectStore.delete(isbn);
// 当 delete() 操作成功完成时触发此事件处理程序。
// 注意:即使没有找到对应的记录,onsuccess 也会被触发,因为 delete() 成功意味着请求本身已成功执行。
deleteRequest.onsuccess = function(event) {
// 输出确认信息到控制台,表示书本已成功删除。
console.log("书本删除成功");
};
// 当整个事务完成时触发此事件处理程序,无论是否有任何操作失败。
// 这是确保所有操作都已完成的最后一个检查点。
transaction.oncomplete = function() {
// 输出信息到控制台,表示所有事务内的操作已完成。
console.log("事务完成");
};
// 如果事务中的任何一个操作发生错误,则触发此事件处理程序。
// 错误可能由多种原因引起,如违反约束、网络问题等。
transaction.onerror = function(event) {
// 输出错误信息到控制台,帮助调试问题。
console.error("删除失败", event.target.error);
};
}
// 使用方法:尝试从数据库中删除一本书。
// 注意:这里的 "db" 参数应该是由 indexedDB.open 成功返回的数据库连接对象,而不是字符串 "MyDatabase"。
// 正确的做法是先打开数据库,然后使用返回的数据库连接对象调用 deleteBook 函数。
var request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
// 获取数据库连接
var db = event.target.result;
// 现在可以安全地调用 deleteBook 函数,并传递实际的数据库连接对象。
deleteBook(db, '123456789');
// 关闭数据库连接(通常在所有操作完成后进行)
// db.close();
};
request.onerror = function(event) {
// 如果打开数据库过程中出现错误,则输出错误信息。
console.error("数据库打开失败", event.target.error);
};
示例 6: 遍历所有记录
function listAllBooks(db) {
// 开始一个新事务,指定要访问的对象存储空间("books"),并设置为只读模式 ("readonly")。
// 由于我们只是遍历数据而不会修改它,所以使用 "readonly" 是合适的。
var transaction = db.transaction(["books"], "readonly");
// 从当前事务中获取名为 "books" 的对象存储空间实例。
var objectStore = transaction.objectStore("books");
// 使用 openCursor() 方法创建一个游标来遍历对象存储中的所有记录。
// 游标允许我们逐条访问对象存储中的记录。
objectStore.openCursor().onsuccess = function(event) {
// 获取游标对象
var cursor = event.target.result;
if (cursor) {
// 如果存在当前记录,则输出书本信息到控制台。
console.log("书名:", cursor.value.title, ", ISBN:", cursor.value.isbn);
// 继续移动游标到下一个记录。
cursor.continue();
} else {
// 如果没有更多记录,输出遍历完毕的信息。
console.log("遍历完毕");
}
};
}
// 使用方法:尝试列出数据库中的所有书籍。
// 注意:这里的 "db" 参数应该是由 indexedDB.open 成功返回的数据库连接对象,而不是字符串 "MyDatabase"。
// 正确的做法是先打开数据库,然后使用返回的数据库连接对象调用 listAllBooks 函数。
var request = indexedDB.open("MyDatabase", 1);
request.onsuccess = function(event) {
// 获取数据库连接
var db = event.target.result;
// 现在可以安全地调用 listAllBooks 函数,并传递实际的数据库连接对象。
listAllBooks(db);
// 关闭数据库连接(通常在所有操作完成后进行)
// db.close();
};
request.onerror = function(event) {
// 如果打开数据库过程中出现错误,则输出错误信息。
console.error("数据库打开失败", event.target.error);
};
这些示例涵盖了创建数据库、插入数据、查询、更新、删除以及遍历所有记录的基本操作。你可以根据自己的需要调整这些代码片段,以适应更复杂的应用场景。请注意,为了使上述代码正常工作,你需要在适当的位置保存 db
变量(即从 indexedDB.open()
的 onsuccess
回调函数中获取到的数据库连接)。
测试代码下载
html5_api代码