IndexedDB 键特性和基本术语
本文介绍了 IndexedDB 的关键特性,并介绍了一些与理解 IndexedDB API 相关的基本术语。
您还会发现以下文章很有用
- 有关如何使用 API 的详细教程,请参阅 使用 IndexedDB。
- 有关 IndexedDB API 的参考文档,请参阅主 IndexedDB API 文章及其子页面,这些页面记录了 IndexedDB 使用的对象类型。
- 有关浏览器如何在后台处理存储数据的更多信息,请阅读 浏览器存储配额和逐出标准。
键特性
IndexedDB 是一种在用户浏览器中持久存储数据的方法。因为它允许您创建具有丰富查询功能的 Web 应用程序,而无需考虑网络可用性,因此这些应用程序可以在联机和脱机状态下工作。IndexedDB 适用于存储大量数据(例如,借阅图书馆中的 DVD 目录)的应用程序,以及不需要持久互联网连接即可工作的应用程序(例如,邮件客户端、待办事项列表和记事本)。
IndexedDB 允许您存储和检索使用“键”索引的对象。您对数据库进行的所有更改都发生在事务中。与大多数 Web 存储解决方案一样,IndexedDB 遵循 同源策略。因此,虽然您可以访问域内的存储数据,但不能跨不同域访问数据。
如果您从使用其他类型的数据库中获得了一些假设,那么在使用 IndexedDB 时可能会感到困惑。因此,以下 IndexedDB 的关键特性需要注意:
- IndexedDB 数据库存储键值对。值可以是复杂的结构化对象,键可以是这些对象的属性。您可以创建使用对象任何属性进行快速搜索以及排序枚举的索引。键可以是二进制对象。
- IndexedDB 基于事务数据库模型构建。您在 IndexedDB 中执行的所有操作都始终发生在 事务 的上下文中。IndexedDB API 提供了许多表示索引、表、游标等的 对象,但这些对象都与特定事务相关联。因此,您不能在事务外部执行命令或打开游标。事务具有明确定义的生命周期,因此尝试在事务完成后使用事务会抛出异常。此外,如果在事务处于活动状态时没有发出新请求,则事务会自动提交。当您考虑如果用户在两个不同的选项卡中同时打开了您的 Web 应用程序的两个实例会发生什么时,这种事务模型非常有用。如果没有事务操作,这两个实例可能会相互干扰彼此的修改。如果您不熟悉数据库中的事务,请阅读 有关事务的维基百科文章。另请参阅“定义”部分下的 事务。
- IndexedDB API 主要是非同步的。API 不会通过返回值为您提供数据;相反,您必须传递一个回调函数。您不会通过同步方式“存储”值到数据库中,也不会“检索”值到数据库中。相反,您“请求”数据库操作发生。操作完成后,您会收到 DOM 事件的通知,并且您收到的事件类型可以让您知道操作是否成功或失败。
- IndexedDB 使用大量请求。请求是接收前面提到的成功或失败 DOM 事件的对象。它们具有
onsuccess
和onerror
属性,您可以对其调用addEventListener()
和removeEventListener()
。它们还具有readyState
、result
和errorCode
属性,这些属性告诉您请求的状态。result
属性特别神奇,因为它可以是许多不同的东西,具体取决于请求是如何生成的(例如,IDBCursor
实例,或者您刚刚插入到数据库中的值的键)。 - IndexedDB 使用 DOM 事件通知您结果何时可用。DOM 事件始终具有
type
属性(在 IndexedDB 中,它最常设置为"success"
或"error"
)。DOM 事件还具有target
属性,该属性指示事件的目标位置。在大多数情况下,事件的target
是作为执行某些数据库操作的结果而生成的IDBRequest
对象。成功事件不会冒泡,也不能被取消。另一方面,错误事件会冒泡,并且可以被取消。这一点非常重要,因为错误事件会中止它们正在运行的事务,除非它们被取消。 - IndexedDB 是面向对象的。IndexedDB 不是一个关系数据库,其中表表示行和列的集合。这种重要且根本的区别会影响您设计和构建应用程序的方式。在传统的关系数据存储中,您将有一个表来存储数据的行集合和命名数据类型的列。另一方面,IndexedDB 要求您为一种数据类型创建对象存储,并将 JavaScript 对象持久化到该存储中。每个对象存储都可以有一组索引,使查询和迭代变得高效。如果您不熟悉面向对象的数据库管理系统,请阅读 有关对象数据库的维基百科文章。
- IndexedDB 不使用结构化查询语言 (SQL)。它使用索引上的查询生成游标,您可以使用该游标迭代结果集。如果您不熟悉 NoSQL 系统,请阅读 有关 NoSQL 的维基百科文章。
-
IndexedDB 遵循同源策略。源是执行脚本的文档的 URL 的域名、应用程序层协议和端口。每个源都有自己关联的一组数据库。每个数据库都有一个名称,用于在源中标识它。对 IndexedDB 实施的安全边界阻止应用程序访问具有不同源的数据。例如,虽然
http://www.example.com/app/
中的应用程序或页面可以从http://www.example.com/dir/
检索数据,因为它们具有相同的源,但它不能从http://www.example.com:8080/dir/
(不同的端口)或https://www.example.com/dir/
(不同的协议)检索数据,因为它们具有不同的源。注意:第三方窗口内容(例如
<iframe>
内容)可以访问其嵌入到的源的 IndexedDB 存储,除非浏览器设置为 从不接受第三方 Cookie(请参阅 Firefox 错误 1147821)。
限制
IndexedDB 旨在涵盖需要客户端存储的大多数情况。但是,它不适用于以下几种情况:
- 国际化排序。并非所有语言都以相同的方式对字符串进行排序,因此不支持国际化排序。虽然数据库无法以特定的国际化顺序存储数据,但您可以自己对从数据库中读取的数据进行排序。
- 同步。API 并非旨在负责与服务器端数据库同步。您必须编写代码以将客户端 IndexedDB 数据库与服务器端数据库同步。
- 全文搜索。API 没有 SQL 中
LIKE
运算符的等效项。
此外,请注意浏览器可以清除数据库,例如在以下情况下:
- 用户请求清除。许多浏览器具有允许用户清除为给定网站存储的所有数据的设置,包括 Cookie、书签、存储的密码和 IndexedDB 数据。
- 浏览器处于隐私浏览模式。某些浏览器具有“隐私浏览”(Firefox)或“隐身”(Chrome)模式。在会话结束时,浏览器会清除数据库。
- 磁盘或配额限制已达到。
- 数据已损坏。
- 对功能进行了不兼容的更改。
确切的情况和浏览器功能会随着时间的推移而发生变化,但浏览器供应商的一般理念是在可能的情况下尽最大努力保留数据。
核心术语
本节定义并解释了与理解 IndexedDB API 相关的核心术语。
数据库
database
信息存储库,通常包含一个或多个 对象存储。每个数据库必须具有以下内容:
- 名称。这标识了特定来源中的数据库,并在其整个生命周期内保持不变。名称可以是任何字符串值(包括空字符串)。
- 当前版本。当数据库首次创建时,如果未另行指定,则其版本为整数 1。每个数据库在任何给定时间只能有一个版本。
数据库连接
通过打开数据库创建的操作。给定的数据库可以同时具有多个连接。
持久化
在 Firefox 中,IndexedDB 过去是持久化的,这意味着在读写事务中,只有当所有数据都保证已刷新到磁盘时,才会触发complete
事件。
从 Firefox 40 开始,IndexedDB 事务已放宽了持久性保证以提高性能(参见Firefox 错误 1112702),这与其他支持 IndexedDB 的浏览器的行为相同。在这种情况下,complete
事件在操作系统被告知写入数据后触发,但可能在数据实际刷新到磁盘之前。因此,事件可能比以前更快地传递,但是,如果操作系统崩溃或在数据刷新到磁盘之前出现系统电源故障,则整个事务存在丢失的小概率。由于此类灾难性事件很少见,因此大多数用户无需进一步担心。
注意:在 Firefox 中,如果您出于某种原因希望确保持久性(例如,您正在存储以后无法重新计算的关键数据),则可以通过使用实验性(非标准)readwriteflush
模式创建事务来强制事务在传递complete
事件之前刷新到磁盘(参见IDBDatabase.transaction
。)这目前是实验性的,并且仅当about:config
中的dom.indexedDB.experimental
首选项设置为true
时才能使用。
索引
索引是用于在另一个对象存储(称为引用对象存储)中查找记录的专用对象存储。索引是一个持久化的键值存储,其记录的值部分是引用对象存储中记录的键部分。每当插入、更新或删除引用对象存储中的记录时,索引中的记录就会自动填充。索引中的每个记录只能指向其引用对象存储中的一个记录,但多个索引可以引用同一个对象存储。当对象存储发生更改时,所有引用该对象存储的索引都会自动更新。
或者,您也可以使用键在对象存储中查找记录。
要了解有关使用索引的更多信息,请参见使用 IndexedDB。有关索引的参考文档,请参见IDBKeyRange。
对象存储
数据存储在数据库中的机制。对象存储永久保存记录,这些记录是键值对。对象存储中的记录根据键按升序排序。
每个对象存储都必须具有在其数据库中唯一的名称。对象存储可以选择具有键生成器和键路径。如果对象存储具有键路径,则它使用内联键;否则,它使用外联键。
有关对象存储的参考文档,请参见IDBObjectStore
。
请求
执行数据库读写操作的操作。每个请求都表示一个读或写操作。
事务
对特定数据库进行的一组原子数据访问和数据修改操作。这是您与数据库中的数据交互的方式。实际上,数据库中任何数据的读取或更改都必须在事务中进行。
数据库连接可以同时具有多个与其关联的活动事务,只要写入事务的范围不重叠即可。事务的范围在创建时定义,确定事务可以与哪些对象存储交互,并在事务的整个生命周期内保持不变。因此,例如,如果数据库连接已经有一个写入事务,其范围仅覆盖flyingMonkey
对象存储,则可以启动第二个事务,其范围为unicornCentaur
和unicornPegasus
对象存储。对于读取事务,您可以拥有多个读取事务——即使是重叠的事务。
事务预计是短暂的,因此浏览器可以终止花费时间过长的事务,以释放长时间运行的事务锁定的存储资源。您可以中止事务,这将回滚对事务中数据库所做的更改。您甚至不必等待事务开始或处于活动状态即可中止它。
事务的三种模式为:readwrite
、readonly
和versionchange
。创建和删除对象存储和索引的唯一方法是使用versionchange
事务。要了解有关事务类型的更多信息,请参阅IndexedDB 的参考文章。
因为所有事情都发生在事务中,所以在 IndexedDB 中这是一个非常重要的概念。要了解有关事务的更多信息,尤其是在它们与版本控制的关系方面,请参见IDBTransaction
,其中也包含参考文档。
版本
当数据库首次创建时,其版本为整数 1。每个数据库在任何给定时间都只有一个版本;数据库不能同时存在于多个版本中。更改版本的唯一方法是以大于当前版本的版本打开它。
键和值
内联键
作为存储值的一部分存储的键。它是使用键路径找到的。可以使用生成器生成内联键。生成键后,可以将其使用键路径存储在值中,也可以将其用作键。
键
用于组织和检索对象存储中存储值的数据值。对象存储可以从三个来源之一派生键:键生成器、键路径或显式指定的值。键必须是数据类型,该数据类型具有大于其前一个数字的数字。对象存储中的每个记录都必须具有在其存储中唯一的键,因此您不能在给定的对象存储中具有多个具有相同键的记录。
键可以是以下类型之一:字符串、日期、浮点数、二进制 Blob 和数组。对于数组,键的范围可以为空值到无穷大。您可以在数组中包含数组。
或者,您也可以使用索引在对象存储中查找记录。
键生成器
以有序序列生成新键的机制。如果对象存储没有键生成器,则应用程序必须为要存储的记录提供键。生成器不会在存储之间共享。这更多是浏览器实现细节,因为在 Web 开发中,您不会真正创建或访问键生成器。
键路径
定义浏览器应在对象存储或索引中从何处提取键。有效的键路径可以包含以下内容之一:空字符串、JavaScript 标识符或用句点分隔的多个 JavaScript 标识符,或包含其中任何一个的数组。它不能包含空格。
外联键
与要存储的值分开存储的键。
值
每条记录都有一个值,该值可以包含任何可以用 JavaScript 表示的内容,包括布尔值、数字、字符串、日期、对象、数组、正则表达式、未定义和 null。
当存储对象或数组时,该对象或数组中的属性和值也可以是任何有效的值。
范围
游标
用于使用键范围迭代多个记录的机制。游标具有指示其正在迭代哪个索引或对象存储的源。它在范围内有一个位置,并沿记录键顺序递增或递减的方向移动。有关游标的参考文档,请参见IDBCursor
。
键范围
用于键的一些数据类型上的连续间隔。可以使用键或键范围从对象存储和索引中检索记录。您可以使用下限和上限限制或过滤范围。例如,您可以迭代键在 x 和 y 之间的所有值。
有关键范围的参考文档,请参见IDBKeyRange
。
范围
事务适用的对象存储和索引集。只读事务的范围可以重叠并同时执行。另一方面,写入事务的范围不能重叠。您仍然可以同时启动多个具有相同范围的事务,但它们只会排队并一个接一个地执行。
后续步骤
了解了 IndexedDB 的关键特征和核心术语后,我们可以进入更具体的内容。有关如何使用 API 的教程,请参见使用 IndexedDB。