Skip to content

IndexedDB - 对象存储

对象存储(Object Store)是 IndexedDB 数据库中核心的数据容器,类似于传统 SQL 数据库中的“表”。单个数据库可以包含多个对象存储,每个对象存储都设计用于保存特定类型的 JavaScript 对象。本章将详细介绍如何创建、定义键以及管理对象存储。

对象存储只能在 onupgradeneeded 事件处理程序中创建或修改。这种设计确保了所有模式(schema)修改都以受控的、版本化的方式进行。你可以使用 IDBDatabase.createObjectStore() 方法创建新的存储。

db.createObjectStore(storeName, options);
  • storeName: 一个字符串,用于命名对象存储。
  • options: 一个可选对象,用于配置主键。两个主要属性是 keyPath 和 autoIncrement。

对象存储中的每条记录都必须有一个唯一键来标识它。IndexedDB 提供了两种主要方式来管理这些主键:

  • 内联键(Key Path): 键是所存储对象的一个属性。你使用 keyPath 选项指定此属性。例如,如果你存储用户对象,你可以使用 email 或 userId 属性作为键路径:{ keyPath: 'userId' }。
  • 外联键(键生成器): 键是单独生成的,并与值一起存储。通过将 autoIncrement 选项设置为 true,你可以让 IndexedDB 为每个新对象自动生成一个递增的整数键。当你的对象没有自然唯一的属性时,这非常有用。

你可以同时使用 keyPath 和 autoIncrement。这对于为对象的特定属性分配唯一的自增 ID 非常有用。

示例:创建带有键路径的对象存储

Section titled “示例:创建带有键路径的对象存储”
const request = indexedDB.open("CustomerDB", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建一个“customers”存储,其中每个对象的“email”属性作为主键。
const store = db.createObjectStore("customers", { keyPath: "email" });
console.log("'customers' object store created successfully.");
};

示例:创建带有自增键的对象存储

Section titled “示例:创建带有自增键的对象存储”
const request = indexedDB.open("TaskDB", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建一个带有自增整数键的“tasks”存储。
// 键将独立于任务对象存储。
const store = db.createObjectStore("tasks", { autoIncrement: true });
// 我们也可以将它们结合使用。
// 这将创建一个“logs”存储,其中每个日志对象都会获得一个“id”属性,
// 该属性会自动分配一个递增的数字。
const logStore = db.createObjectStore("logs", { keyPath: 'id', autoIncrement: true });
console.log("'tasks' and 'logs' object stores created.");
};

虽然主键允许你高效地检索单个记录,但索引可以根据对象的其他属性实现快速查找。索引就像是数据的独立、排序视图。你可以在对象存储实例上使用 createIndex() 方法创建索引,同样是在 onupgradeneeded 事件中。

objectStore.createIndex(indexName, keyPath, options);
  • indexName: 索引的名称。
  • keyPath: 存储对象中用于排序和查找的属性。这也可以是用于复合索引的数组。
  • options: 包含配置属性的可选对象,例如 unique(确保索引中的所有值都是唯一的)和 multiEntry(用于索引数组值)。

示例:创建索引和填充初始数据

Section titled “示例:创建索引和填充初始数据”

此示例设置了一个 products 存储,并在 category 和 price 上创建了索引。它还展示了如何在模式创建后立即添加初始数据。

const request = indexedDB.open("StoreDB", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建对象存储
const store = db.createObjectStore("products", { keyPath: "id" });
// 在“category”属性上创建索引
store.createIndex("by_category", "category", { unique: false });
// 在“price”属性上创建索引
store.createIndex("by_price", "price", { unique: false });
console.log('Object store and indexes are ready.');
// onupgradeneeded 的事务仍然处于活动状态,
// 所以我们可以在这里添加初始数据。
const initialProducts = [
{ id: 'p1', name: 'Modern JavaScript', category: 'books', price: 29.99 },
{ id: 'p2', name: 'Web Development', category: 'books', price: 39.99 },
{ id: 'p3', name: 'Mechanical Keyboard', category: 'electronics', price: 150.00 }
];
// 要添加数据,我们需要通过事务再次访问存储
const productStore = event.target.transaction.objectStore('products');
initialProducts.forEach(product => {
productStore.add(product);
});
console.log('Initial products seeded.');
};

如果不再需要某个对象存储,你可以使用 db.deleteObjectStore() 方法删除它。与创建类似,这也必须在 onupgradeneeded 事件中完成,这意味着你必须使用新的版本号打开数据库。

// 假设“StoreDB”当前版本为 1 且包含一个“obsoleteStore”。
// 要删除它,我们需要使用更高的版本号打开数据库。
const request = indexedDB.open("StoreDB", 2);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 在尝试删除之前,检查存储是否存在
if (db.objectStoreNames.contains('obsoleteStore')) {
db.deleteObjectStore("obsoleteStore");
console.log("'obsoleteStore' has been deleted.");
}
};