第一部分:基础概念

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 的核心优势在于:

  • 结构化存储:存储 JavaScript 对象,而非仅仅是字符串
  • 索引支持:可以对对象的属性建立索引,实现高效查询
  • 事务支持:所有操作都在事务中进行,保证数据完整性
  • 异步 API:不会阻塞主线程,适合处理大量数据
  • 大容量:相比 LocalStorage,可以存储更多数据

IndexedDB 的发展历程:

  • 2010年:W3C 开始制定 IndexedDB 规范,目标是替代 Web SQL Database
  • 2011-2012年:Chrome、Firefox 开始实验性支持
  • 2013年:IE10 引入 IndexedDB 支持
  • 2015年:成为 W3C 推荐标准
  • 2017年:Safari 10 加入支持,实现跨浏览器兼容
  • 2018年:Promise 封装(IndexedDB 2.0)普及
  • 2022年:IndexedDB API 规范化,idbjd 等库成熟

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

  • 每个数据库有一个名称(字符串)
  • 每个数据库有一个版本号(整数,从1开始)
  • 数据库中包含多个对象存储空间
  • 同一域名下,不同页面可以访问同一个数据库

数据库命名规范:

  • 使用有意义的名称,如 “EmailClientDB”、“GameProgressDB”
  • 避免使用特殊字符和空格
  • 考虑添加版本标识,如 “MyApp_v2”

版本号策略:

  • 版本号必须是正整数(1, 2, 3…)
  • 每次修改数据库结构(添加/删除对象存储、索引)必须增加版本号
  • 版本号只能增加,不能减少
  • 建议使用语义化版本控制的思想:主版本.次版本 映射到单一整数

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

  • 存储的是 JavaScript 对象(键值对集合)
  • 每个对象必须有一个主键(key)
  • 主键可以是对象的一个属性,也可以是独立生成的
  • 同一对象存储空间中的对象不需要有相同的结构(schema-less)

主键类型:

  • 内联键(in-line key):使用对象的某个属性作为主键
    // keyPath 指定主键属性
    store.createObjectStore('users', { keyPath: 'userId' });
 
  • 外联键(out-of-line key):主键与对象数据分离
    // 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 });
 
  • 多入口索引(MultiEntry):用于数组属性
    // 如果 tags 是数组 ['js', 'web', 'api']
    // 会创建三个索引条目
    store.createIndex('tagIndex', 'tags', { multiEntry: true });
 

事务是 IndexedDB 中所有数据操作的执行单元:

  • ACID 特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
  • 三种模式
    • readonly:只读,可以并发执行多个
    • readwrite:读写,同一对象存储空间同时只能有一个
    • versionchange:版本变更,用于 onupgradeneeded 回调

事务生命周期:

// 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 的存储有一定限制:

  • 临时存储(Temporary):可以被浏览器自动清理
    • Chrome:可用磁盘空间的 60%
    • Firefox:可用磁盘空间的 50%
    • Safari:约 1GB
  • 持久存储(Persistent):不会被浏览器自动清理
    • 需要用户授权
    • API:`navigator.storage.persist()`

检查存储配额:

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 的场景:

  • 简单键值对:如果只需要存储少量字符串,使用 LocalStorage
  • 会话数据:需要页面关闭后清除的数据,使用 SessionStorage
  • 敏感数据:不要在 IndexedDB 中存储密码、密钥等敏感信息
  • 大文件存储:对于视频、大型图片,使用 OPFS 或 Cache API
  • 需要 SQL 查询:IndexedDB 不支持 SQL,考虑使用 Web SQL(已废弃)或 SQLite WASM
特性 IndexedDB LocalStorage
存储容量 250MB+ 5-10MB
数据类型 结构化对象 仅字符串
同步/异步 异步 同步
索引 支持 不支持
事务 支持 不支持
性能 适合大量数据 适合少量数据
使用复杂度 较高 简单

选择建议:

  • 数据量 > 5MB → IndexedDB
  • 需要索引或查询 → IndexedDB
  • 简单配置数据 → LocalStorage

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

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

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

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

IndexedDB 遵循同源策略:

  • 协议、域名、端口必须完全相同
  • 不同页面(同一域名)可以访问相同数据库
  • 子域名之间无法共享数据(除非使用 document.domain)

浏览器的隐私/无痕模式对 IndexedDB 的影响:

  • 数据通常只在会话期间保留
  • 关闭隐私窗口后数据会被清除
  • 某些浏览器可能完全禁用 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 本身不提供加密,但你可以:

  • 使用 Web Crypto API 加密敏感字段
  • 只存储非敏感的引用,敏感数据存储在服务器
  • 使用 SubtleCrypto 进行客户端加密

原生 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 的基础概念:

  • IndexedDB 是浏览器提供的结构化数据存储方案
  • 核心概念包括数据库、对象存储、索引、事务和游标
  • 适用于需要存储大量结构化数据的离线应用场景
  • 与 LocalStorage、Cache API 各有适用场景
  • 遵循同源策略,在隐私模式下可能受限

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • indexeddb/part01-introduction.txt
  • 最后更改: 2026/04/27 19:50
  • 张叶安