目录

第一部分:基础概念

1.1 什么是 IndexedDB

IndexedDB(索引数据库)是浏览器提供的一种客户端结构化数据存储解决方案。它是 Web Storage(LocalStorage 和 SessionStorage)的升级版,专为存储大量结构化数据而设计。

1.1.1 IndexedDB 的定位

在现代 Web 技术栈中,IndexedDB 位于以下位置:

存储技术 容量 数据结构 适用场景
Cookies 4KB 字符串键值对 会话状态、跟踪标识
LocalStorage 5-10MB 字符串键值对 简单配置、用户偏好
SessionStorage 5-10MB 字符串键值对 临时会话数据
IndexedDB 250MB+ (依浏览器而定) 结构化对象 复杂数据、离线应用
Cache API 磁盘空间限制 HTTP 请求/响应 Service Worker 缓存
Origin Private File System 磁盘空间限制 文件系统 大文件存储

IndexedDB 的核心优势在于:

1.1.2 IndexedDB 的历史演进

IndexedDB 的发展历程:

1.2 核心概念详解

1.2.1 数据库(Database)

IndexedDB 中的数据库类似于关系型数据库中的数据库概念,但更为简单:

数据库命名规范:

版本号策略:

1.2.2 对象存储空间(Object Store)

对象存储空间是 IndexedDB 的核心数据结构,类似于关系型数据库中的

主键类型:

    // keyPath 指定主键属性
    store.createObjectStore('users', { keyPath: 'userId' });
 
    // autoIncrement 让数据库自动生成递增整数作为主键
    store.createObjectStore('logs', { autoIncrement: true });
 
    // userId + projectId 组合成唯一主键
    store.createObjectStore('memberships', { 
      keyPath: ['userId', 'projectId'] 
    });
 

1.2.3 索引(Index)

索引用于加速对非主键属性的查询:

索引的类型:

    store.createIndex('ageIndex', 'age', { unique: false });
 
    store.createIndex('emailIndex', 'email', { unique: true });
 
    // 如果 tags 是数组 ['js', 'web', 'api']
    // 会创建三个索引条目
    store.createIndex('tagIndex', 'tags', { multiEntry: true });
 

1.2.4 事务(Transaction)

事务是 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);

1.2.5 游标(Cursor)

游标用于遍历对象存储空间或索引中的记录:

游标类型:

1.3 IndexedDB 的架构设计

1.3.1 整体架构

IndexedDB 的架构可以分为以下几个层次:

层次 职责 对应 API
应用层 业务逻辑、数据模型 开发者自定义代码
封装层(可选) Promise 封装、ORM 抽象 idb、Dexie.js、localForage
API 原生 IndexedDB API indexedDB.open(), IDBDatabase
事务层 事务管理、并发控制 IDBTransaction
存储层 数据持久化、索引管理 浏览器内部实现

1.3.2 异步事件模型

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;
  // 在这里创建对象存储和索引
};

1.3.3 存储配额管理

浏览器对 IndexedDB 的存储有一定限制:

检查存储配额:

navigator.storage.estimate().then(estimate => {
  console.log(`已使用: ${estimate.usage} bytes`);
  console.log(`配额: ${estimate.quota} bytes`);
  console.log(`剩余: ${estimate.quota - estimate.usage} bytes`);
});

1.4 适用场景分析

1.4.1 典型应用场景

场景一:离线 Web 应用

PWA(渐进式 Web 应用)使用 IndexedDB 存储:

场景二:数据密集型应用

场景三:游戏开发

场景四:内容管理系统

1.4.2 不适用场景

不要使用 IndexedDB 的场景:

1.5 IndexedDB 与其他存储技术对比

1.5.1 与 LocalStorage 对比

特性 IndexedDB LocalStorage
存储容量 250MB+ 5-10MB
数据类型 结构化对象 仅字符串
同步/异步 异步 同步
索引 支持 不支持
事务 支持 不支持
性能 适合大量数据 适合少量数据
使用复杂度 较高 简单

选择建议:

1.5.2 与 Cache API 对比

Cache API 主要用于 Service Worker 缓存 HTTP 请求:

特性 IndexedDB Cache API
设计目的 结构化数据存储 HTTP 响应缓存
存储内容 JS 对象 Request/Response 对
索引 支持 不支持
查询能力 强大 简单 URL 匹配
与 SW 集成 可配合使用 原生支持

1.5.3 与 OPFS 对比

Origin Private File System 提供类似文件系统的 API:

特性 IndexedDB OPFS
数据模型 记录/对象 文件/目录
随机访问 通过游标 通过文件句柄
流式处理 不支持原生 支持
大文件 不适合 适合
结构化查询 强大 不支持

1.6 安全性考虑

1.6.1 同源策略

IndexedDB 遵循同源策略:

1.6.2 隐私模式

浏览器的隐私/无痕模式对 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);
    }
  });
}

1.6.3 数据加密

虽然 IndexedDB 本身不提供加密,但你可以:

1.7 现代 IndexedDB 封装库

1.7.1 为什么需要封装

原生 IndexedDB API 的特点:

1.7.2 主流封装库

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();

1.8 本章小结

本章介绍了 IndexedDB 的基础概念: