indexeddb 不能替代离线存储,必须与 cache api 协同使用;前者存结构化业务数据,后者缓存 request/response 以支持静态资源和 api 响应离线访问。

IndexedDB 不能替代离线存储,它只是离线存储方案中负责“结构化数据持久化”的一环;真正完整的离线体验,必须和 Cache API 协同使用。
为什么单独用 IndexedDB 做不了离线存储
IndexedDB 存的是解析后的 JS 对象(比如 {id: 1, title: '待办', syncStatus: 'pending'}),但它不缓存网络请求本身。这意味着:
- 页面首次加载时,
index.html、main.js、style.css这些资源仍需联网获取——没它们,JS 根本跑不起来,IndexedDB 连初始化的机会都没有 - 调用
fetch('/api/todos')时,如果网络断开,请求直接失败,IndexedDB 里再有数据也救不了接口层的空白 - HTTP 缓存语义(如
ETag、Cache-Control、Vary)在 IndexedDB 里完全丢失,无法复用服务端协商缓存逻辑
Cache API 和 IndexedDB 各自该存什么
二者分工明确,混用或互换都会出问题:
-
Cache API:只存Request/Response对,适合静态资源和原始 API 响应体(response.clone()后才能存) -
IndexedDB:只存结构化业务数据,比如用户草稿、消息队列、带索引的待办列表,支持get、getAll、openCursor、多字段createIndex - 错误做法:
caches.put('/drafts', new Response(JSON.stringify(draft)))—— Cache API 没查询能力,存了也白存;indexedDB.put(rawResponseBlob)—— IndexedDB 不理解 HTTP 头,后续没法做条件匹配
典型协同流程:一次 fetch,两处落库
关键点在于“响应体只能读一次”,必须用 response.clone() 分流:
const response = await fetch('/api/todos');
const cachedResponse = response.clone(); // 供 cache.put()
const data = await response.json(); // 供 indexedDB.put()
await caches.open('api-cache').put('/api/todos', cachedResponse);
// …再把 data 写入 IndexedDB 的 'todos' objectStore
后续离线访问时:
- 页面资源走
Cache API(由 Service Worker 拦截fetch事件自动返回) - 界面渲染直接从 IndexedDB 读取
data,无需等待网络 - 用户新增条目先写 IndexedDB 并标记
syncStatus: 'pending',网络恢复后由同步逻辑批量提交
容易被忽略的兼容性与陷阱
实际部署时这几个点最容易卡住:
-
navigator.onLine不可靠,必须配合fetch()超时 +catch判定真实离线状态 - Service Worker 的
install阶段不能调用indexedDB.open(),会触发跨上下文错误;数据库初始化必须放在activate或主页面 JS 中 - IndexedDB 的
onupgradeneeded是唯一能建表/加索引的地方,版本号升错(比如从 1 直跳到 3)会导致中间迁移逻辑跳过 -
Cache API的put()接收的是Response实例,传new Response(JSON.stringify(...))会丢失Content-Type,建议显式设置:new Response(JSON.stringify(...), {headers: {'Content-Type': 'application/json'}})
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











