IndexedDB(索引数据库)是浏览器提供的一种客户端结构化数据存储解决方案。它是 Web Storage(LocalStorage 和 SessionStorage)的升级版,专为存储大量结构化数据而设计。
在现代 Web 技术栈中,IndexedDB 位于以下位置:
| 存储技术 | 容量 | 数据结构 | 适用场景 |
|---|---|---|---|
| Cookies | 4KB | 字符串键值对 | 会话状态、跟踪标识 |
| LocalStorage | 5-10MB | 字符串键值对 | 简单配置、用户偏好 |
| SessionStorage | 5-10MB | 字符串键值对 | 临时会话数据 |
| IndexedDB | 250MB+ (依浏览器而定) | 结构化对象 | 复杂数据、离线应用 |
| Cache API | 磁盘空间限制 | HTTP 请求/响应 | Service Worker 缓存 |
| Origin Private File System | 磁盘空间限制 | 文件系统 | 大文件存储 |
IndexedDB 的核心优势在于:
IndexedDB 的发展历程:
IndexedDB 中的数据库类似于关系型数据库中的数据库概念,但更为简单:
数据库命名规范:
版本号策略:
对象存储空间是 IndexedDB 的核心数据结构,类似于关系型数据库中的表:
主键类型:
// keyPath 指定主键属性 store.createObjectStore('users', { keyPath: 'userId' });
// autoIncrement 让数据库自动生成递增整数作为主键 store.createObjectStore('logs', { autoIncrement: true });
// userId + projectId 组合成唯一主键 store.createObjectStore('memberships', { keyPath: ['userId', 'projectId'] });
索引用于加速对非主键属性的查询:
索引的类型:
store.createIndex('ageIndex', 'age', { unique: false });
store.createIndex('emailIndex', 'email', { unique: true });
// 如果 tags 是数组 ['js', 'web', 'api'] // 会创建三个索引条目 store.createIndex('tagIndex', 'tags', { multiEntry: true });
事务是 IndexedDB 中所有数据操作的执行单元:
事务生命周期:
// 1. 开启事务 const transaction = db.transaction(['store1', 'store2'], 'readwrite'); // 2. 获取对象存储空间引用 const store1 = transaction.objectStore('store1'); const store2 = transaction.objectStore('store2'); // 3. 执行操作 store1.add(data1); store2.put(data2); // 4. 监听完成和错误 transaction.oncomplete = () => console.log('事务完成'); transaction.onerror = (e) => console.error('事务错误', e);
游标用于遍历对象存储空间或索引中的记录:
游标类型:
IndexedDB 的架构可以分为以下几个层次:
| 层次 | 职责 | 对应 API |
|---|---|---|
| 应用层 | 业务逻辑、数据模型 | 开发者自定义代码 |
| 封装层(可选) | Promise 封装、ORM 抽象 | idb、Dexie.js、localForage |
| API 层 | 原生 IndexedDB API | indexedDB.open(), IDBDatabase |
| 事务层 | 事务管理、并发控制 | IDBTransaction |
| 存储层 | 数据持久化、索引管理 | 浏览器内部实现 |
IndexedDB 使用基于事件的异步 API:
const request = indexedDB.open('myDB', 1); // 请求对象有三种主要事件 request.onsuccess = function(event) { // 请求成功 const db = event.target.result; }; request.onerror = function(event) { // 请求失败 console.error('错误:', event.target.error); }; request.onupgradeneeded = function(event) { // 数据库需要升级(版本号增加时触发) const db = event.target.result; // 在这里创建对象存储和索引 };
浏览器对 IndexedDB 的存储有一定限制:
检查存储配额:
navigator.storage.estimate().then(estimate => { console.log(`已使用: ${estimate.usage} bytes`); console.log(`配额: ${estimate.quota} bytes`); console.log(`剩余: ${estimate.quota - estimate.usage} bytes`); });
场景一:离线 Web 应用
PWA(渐进式 Web 应用)使用 IndexedDB 存储:
场景二:数据密集型应用
场景三:游戏开发
场景四:内容管理系统
不要使用 IndexedDB 的场景:
| 特性 | IndexedDB | LocalStorage |
|---|---|---|
| 存储容量 | 250MB+ | 5-10MB |
| 数据类型 | 结构化对象 | 仅字符串 |
| 同步/异步 | 异步 | 同步 |
| 索引 | 支持 | 不支持 |
| 事务 | 支持 | 不支持 |
| 性能 | 适合大量数据 | 适合少量数据 |
| 使用复杂度 | 较高 | 简单 |
选择建议:
Cache API 主要用于 Service Worker 缓存 HTTP 请求:
| 特性 | IndexedDB | Cache API |
|---|---|---|
| 设计目的 | 结构化数据存储 | HTTP 响应缓存 |
| 存储内容 | JS 对象 | Request/Response 对 |
| 索引 | 支持 | 不支持 |
| 查询能力 | 强大 | 简单 URL 匹配 |
| 与 SW 集成 | 可配合使用 | 原生支持 |
Origin Private File System 提供类似文件系统的 API:
| 特性 | IndexedDB | OPFS |
|---|---|---|
| 数据模型 | 记录/对象 | 文件/目录 |
| 随机访问 | 通过游标 | 通过文件句柄 |
| 流式处理 | 不支持原生 | 支持 |
| 大文件 | 不适合 | 适合 |
| 结构化查询 | 强大 | 不支持 |
IndexedDB 遵循同源策略:
浏览器的隐私/无痕模式对 IndexedDB 的影响:
function checkIndexedDBSupport() { return new Promise((resolve) => { try { const request = indexedDB.open('__test__'); request.onsuccess = () => resolve(true); request.onerror = () => resolve(false); } catch (e) { resolve(false); } }); }
虽然 IndexedDB 本身不提供加密,但你可以:
原生 IndexedDB API 的特点:
idb — 轻量级 Promise 封装:
import { openDB } from 'idb'; const db = await openDB('myDB', 1, { upgrade(db) { db.createObjectStore('users', { keyPath: 'id' }); } }); await db.add('users', { id: 1, name: '张三' });
Dexie.js — 功能丰富的 IndexedDB 封装:
const db = new Dexie('myDB'); db.version(1).stores({ users: '++id, name, age' }); await db.users.add({ name: '张三', age: 25 }); const youngUsers = await db.users.where('age').below(30).toArray();
本章介绍了 IndexedDB 的基础概念: