indexeddb构建缓存架构的核心是建立有生命周期、可查询、能降级、不阻塞的本地服务端系统;需按业务域拆分object store、索引驱动查询、分批写入、主动控容清理。

用 IndexedDB 构建本地化应用缓存架构,核心不是“把数据存下来”,而是建立一套有生命周期、可查询、能降级、不拖慢页面的缓存系统。它本质上是把浏览器变成一个轻量级本地服务端——数据就绪、索引就位、事务可控、容量可管。
按业务域建模:拆分 Object Store,避免大一统仓库
不要用一个 cache store 存所有接口响应。不同数据类型天然存在访问模式、更新频率和过期策略差异:
-
articles:存新闻/内容详情,主键用
id,索引建在updatedAt和category上,支持“最新”“分类列表”两类高频查询 -
api_responses:缓存通用接口返回(如用户信息、配置项),主键用请求 URL 的哈希值,加
expiresAt字段用于 TTL 清理 -
offline_queues:专用于暂存离线时的写操作(如提交表单),带
retryCount和createdAt,便于重试与超时丢弃
这样设计后,清理某类缓存不会波及其他模块,版本升级也能独立迭代 schema。
索引驱动查询:没有索引的 getRange 等于遍历全量
IndexedDB 的查询性能几乎完全取决于索引。对未建索引字段调用 index.openCursor(IDBKeyRange.bound(...)),等同于前端做 for 循环——10 万条记录可能卡死页面。
- 对时间范围查询(如“最近 3 天订单”),必须为
createdAt或updatedAt建普通索引,并用IDBKeyRange.bound(start, end) - 对多条件组合(如 status = "pending" AND priority > 3),无法直接建复合索引,应改用单字段索引 + 游标过滤,或前置归一化字段(如存
status_priority字符串) - 避免在长文本字段(
content、description)上建索引;全文搜索交给FlexSearch或tantivy-wasm这类专用库
写入不阻塞:分批 + 微任务调度 + 明确事务范围
一次性写入数千条缓存数据极易触发主线程冻结。关键不是“少写”,而是“写得聪明”:
- 将大数据集切分为每批 500–1000 条,每批使用独立
readwrite事务 - 批次之间用
queueMicrotask或setTimeout(() => {}, 0)让出控制权,保证 UI 可响应 - 开启事务时务必显式声明 scope:
db.transaction(['articles'], 'readwrite'),防止意外锁住offline_queues等其他 store - 插入前检查是否已存在(用
get()或游标定位),避免重复写入放大 I/O 压力
主动控容与清理:缓存不是垃圾桶,是带保质期的货架
IndexedDB 不会自动释放空间,长期运行的应用可能因缓存膨胀导致写入失败或被浏览器限频。
- 每条缓存记录插入时附带
createdAt和expiresAt字段,例如文章缓存设为 7 天,API 响应设为 5 分钟 - 定期(如每天首次打开页面时)启动后台清理任务:用游标遍历
api_responses中expiresAt 的项并 <code>delete - 监听
navigator.storage.estimate(),当使用率超 80% 时,触发分级清理:先删offline_queues(临时性最强),再删过期api_responses,最后考虑压缩articles(保留最新 N 条) - 敏感字段(token、手机号、身份证号)绝不明文存入;必须缓存时,用
SubtleCrypto.encrypt()加密后再写入











