indexeddb 是具备事务、索引、异步和结构化能力的客户端数据库,需按业务域建模、封装 promise 接口、结合不可变数据、主动管理生命周期并加密敏感状态。

IndexedDB 是 Web 应用实现状态持久化的首选方案,它不是“高级 localStorage”,而是真正具备事务、索引、异步和结构化能力的客户端数据库。关键不在于能不能存,而在于怎么设计、怎么写、怎么管——状态数据一旦结构混乱或写入失控,轻则卡顿,重则丢失。
按业务域建模,避免单表大杂烩
状态数据往往跨多个模块(如用户偏好、编辑草稿、离线队列、UI 展开状态),直接塞进一个 object store 会带来维护难、清理难、查询慢三大问题:
- 为每类状态单独建 store:例如
ui_state存折叠面板、user_settings存主题/语言、drafts存未提交表单 - 主键尽量用语义 ID(如
"sidebar:theme"或"user:123:notifications"),而非 autoIncrement 数字——后者在多端同步或离线写入时极易冲突 - 对需按字段筛选的状态项(如“所有未同步的草稿”),在对应字段上显式建索引:
store.createIndex('syncStatus', 'syncStatus')
封装成 Promise 接口,统一读写逻辑
原生 IndexedDB 基于事件回调,嵌套深、易出错。建议封装基础操作,让状态存取像调用函数一样自然:
- 打开数据库时统一处理版本升级逻辑,自动创建/迁移 store 和索引
- 读取状态用
get(key),写入用put(value, key),删除用delete(key),全部返回 Promise - 批量操作(如初始化一组默认状态)走
transaction.objectStore().putAll(),避免多次开事务 - 示例:保存 UI 折叠状态
saveState('sidebar:collapsed', true)→ 内部自动序列化、事务写入、错误捕获
结合不可变数据结构,保障状态可追溯
状态变更若直接 mutate 对象,容易引发意外覆盖或难以回溯。配合 immutable-js 或原生 structuredClone + Object.freeze 可显著提升可靠性:
- 读取后用
Immutable.fromJS()转为不可变结构,所有更新基于.set()、.merge()等纯函数 - 写入前调用
.toJS()转回普通对象,再存入 IndexedDB(注意:IndexedDB 原生支持 Date、ArrayBuffer、TypedArray 等,无需 JSON 序列化) - 对高频更新状态(如实时编辑光标位置),可加时间戳 + 版本号字段,便于冲突检测与回滚
主动管理生命周期,防止无限膨胀
状态数据不会自动过期。长期运行的应用若无清理机制,可能占满配额甚至触发浏览器限制:
- 为每条状态记录添加
createdAt和expiresAt字段(如 UI 状态保留 90 天,草稿保留 7 天) - 启动时或空闲时用游标扫描过期项:
cursor.openKeyCursor(IDBKeyRange.upperBound(Date.now())),批量 delete - 监听
navigator.storage.estimate(),当用量 >80% 时触发分级清理(先删临时状态,再删旧草稿) - 敏感状态(如 token、临时授权码)绝不明文存入;必须存时,用
window.crypto.subtle.encrypt()加密后再写











