IndexedDB 是需结构设计和生命周期管理的本地数据库系统,非 localStorage 替代品;应按业务域建独立对象仓库、用业务主键作 keyPath、为高频字段建索引、分批事务写入、主动清理与容量监控、敏感数据加密存储。

IndexedDB 不是 localStorage 的替代品,而是一套需要结构设计和生命周期管理的本地数据库系统。要真正实现可靠、可维护的离线数据持久化,关键不在“能不能存”,而在“怎么组织、怎么读、怎么清理、怎么同步”。
按业务域建对象仓库,避免单表膨胀
把用户设置、聊天记录、草稿、离线任务全塞进一个 store,短期看似省事,长期必然导致性能下降和清理困难。
- 为每个功能模块单独建 store:比如 user_settings、chat_messages、draft_notes、offline_actions
- keyPath 优先用业务主键(如 message_id 或 note_uuid),不用 autoIncrement——后者在多端编辑或断网补传时容易冲突
- 临时性数据(如上传队列)建议独立 store,并配专属清理逻辑,不影响核心数据稳定性
索引要精,但关键字段必须覆盖
没索引的查询等于全表扫描。10 万条记录下,一次无索引的 getAll() 就可能卡住界面。
- 为高频筛选字段建索引:status、category、updated_at、tenant_id(均设
unique: false) - 时间范围查询(如“最近 7 天”)必须配合
IDBKeyRange.bound(start, end),且前提是该字段已建索引 - 避免对长文本字段(如 content、description)建索引;全文检索交给 FlexSearch 或 tantivy-wasm 等专用库
分批事务写入,守住主线程响应性
一次性写入几万条数据,不仅慢,还可能被浏览器中断。高吞吐靠节奏,不靠堆量。
- 将大数据集切分为每批 500–2000 条,统一用
transaction.objectStore().add()批量提交 - 每批写完后让出主线程:
queueMicrotask(() => nextBatch())或setTimeout(() => nextBatch(), 0) - 开启事务时明确 scope:
db.transaction(['notes'], 'readwrite'),防止意外锁住其他 store 影响并行操作
主动清理 + 容量监控,防静默爆 quota
IndexedDB 不会自动释放空间。长期运行的应用若无清理机制,某次更新后可能突然报 QuotaExceededError。
- 为缓存类数据加 expiresAt 字段,定期用游标遍历并
delete过期项 - 调用
navigator.storage.estimate()获取当前用量,当usage / quota > 0.8时触发分级清理(先删日志,再删旧快照) - 敏感数据(token、手机号、密钥)绝不裸存;必须存时,用 Web Crypto API 加密后再写入

















