IndexedDB需工程化设计:按业务域分store、用业务主键作keyPath、关键字段建索引、分批事务写入、主动清理与容量监控、敏感数据加密存储。

IndexedDB 不是“带索引的 localStorage”,而是一个需要像服务端数据库一样认真设计的本地数据系统。结构没想清楚,后期写得越猛,崩得越快。
按业务域划分 Object Store
别把所有数据塞进一个 store。用户配置、聊天消息、草稿、离线任务混在一起,会导致查询慢、清理难、事务锁冲突。
推荐做法:
- 每个核心业务模块对应一个独立 store,比如 user_settings、chat_messages、draft_documents
- 临时性数据(如上传队列)单独建 store,并配专属过期策略,不影响主数据稳定性
- 避免运行时动态创建 store——它只能在
onupgradeneeded中定义,频繁升级版本会破坏体验和兼容性
键路径与索引要服务真实查询场景
keyPath 决定怎么找单条记录,索引决定怎么批量筛选。选错就等于给高速路修羊肠小道。
关键原则:
- keyPath 优先用业务主键(如 message_id、draft_uuid),不用 autoIncrement 数字——后者在多端同步或补传时极易重复
- 为高频过滤字段建索引:status、category、updated_at、tenant_id,都设
unique: false - 时间范围查(如“最近 3 天”)必须配合
IDBKeyRange.bound(start, end),且 updated_at 已建索引 - 别在长文本字段(content、description)上建索引;全文检索交给 FlexSearch 或 tantivy-wasm
事务节奏与写入吞吐要可控
一次写几万条不是高性能,是高风险。长时间事务可能被浏览器中断,还会卡住 UI。
稳妥做法:
- 大数据写入切分为每批 500–2000 条,用
add()批量提交 - 每批写完后让出主线程:用
queueMicrotask(() => nextBatch())或setTimeout(() => nextBatch(), 0) - 开启事务时明确指定 scope:
db.transaction(['chat_messages'], 'readwrite'),不锁无关 store
生命周期管理不能靠运气
IndexedDB 不会自动腾空间。长期运行的应用若无主动治理,某天就会突然报 QuotaExceededError。
必须落地的机制:
- 为缓存类数据加 expiresAt 字段,定期用游标遍历并
delete过期项 - 调用
navigator.storage.estimate()监控用量,当usage / quota > 0.8时触发分级清理 - 敏感字段(token、手机号、密钥)绝不裸存;必须存时,用 Web Crypto API 加密后再写入

















