indexeddb 完全适合存大型 json 配置文件,但必须拆解结构建索引、用事务封装并直接存对象而非字符串,否则查不出、改不动、写失败不报错;localstorage 超2mb易静默失败且不支持字段查询。

直接结论:IndexedDB 完全适合存大型 JSON 配置文件,但不能直接 put(JSON.stringify(obj)) —— 必须拆解结构、建索引、用事务封装,否则查不出、改不动、甚至写失败都不报错。
为什么 localStorage 会在这里翻车
大型 JSON 配置文件(比如含嵌套对象、数组、Blob 引用、千级字段)一旦超过 2MB,localStorage.setItem() 就可能静默失败(尤其 Safari 和 iOS WKWebView);即使成功,每次 getItem() 都要同步解析整个字符串,主线程卡顿明显;更关键的是,它不支持按字段查询——你想找 "lastUsedModel": "flux-dev" 这条记录?得全量 load + JSON.parse() + find(),O(n) 白给。
必须建 objectStore + index 的真实原因
- 配置文件通常有自然主键,比如
id、timestamp或hash,直接设为keyPath,避免手动管理 key - 高频查询字段(如
category、isDefault、updatedAt)必须显式调用createIndex(),否则openCursor()扫全表,10 万条配置就是 10 万次遍历 - 不建索引的
getAll()在数据量 >5000 条时,Chrome DevTools 的 “Application → IndexedDB” 面板可能直接卡死,不是你的代码问题,是浏览器 UI 渲染器扛不住
存 JSON 对象本身,而不是字符串
IndexedDB 原生支持 JavaScript 对象,不要对大型 JSON 做 JSON.stringify() 再存——这会让索引失效、无法用游标精准定位字段、增加序列化开销。正确做法是把 JSON 当成普通 JS 对象传给 put():
const transaction = db.transaction(['configs'], 'readwrite');
const store = transaction.objectStore('configs');
// ✅ 直接存对象,字段可被索引命中
store.put({
id: 'cfg-2026-04-18-abc123',
category: 'image-generation',
isDefault: true,
model: 'sdxl-turbo',
size: [1024, 1024],
updatedAt: Date.now(),
// 可直接存 Uint8Array / Blob(如果配置里带预览图二进制)
}, 'cfg-2026-04-18-abc123');
注意:put() 的第二个参数是 key,必须与 keyPath 一致或显式传入,否则写入后查不到。
离线写入失败的三个隐藏雷区
-
onerror不捕获所有错误:事务中某个put()失败,transaction.onerror会触发,但request.onerror不一定——优先监听事务级错误 - iOS WKWebView 在低电量模式或后台被系统杀死后,IndexedDB 数据可能被清空,必须在
onupgradeneeded中检查并重建 schema,不能假设 DB 一定存在 - 大 JSON 对象若含
undefined、function、Date实例等非结构化值,put()会静默跳过该字段(不是报错),建议入库前用JSON.parse(JSON.stringify(obj))做一次安全净化
最常被忽略的一点:IndexedDB 没有“自动压缩”或“自动去重”,同一份大型配置反复写入,会占满空间却不提示——上线前务必加 db.storage.estimate() 监控使用率。











