能存,但需结构化拆解、分片写入、预置索引;直接put大json会卡死页面、触发ios清理或chrome中断事务,且无法高效查询。

能存,但直接 put() 一个几十 MB 的配置 JSON 文件会卡死页面、触发 iOS 清理、甚至被 Chrome 主动中断事务。真正可行的方式是:结构化拆解 + 分片写入 + 索引预置,而不是把它当 blob 塞进去。
为什么不能直接存整个 JSON 对象
IndexedDB 不解析内容,put() 传入的只是 JS 对象引用;浏览器底层序列化时,若单条记录超 16MB(Chrome 实测阈值),事务会静默失败;iOS 在后台或低电量下会无预警清空整个 DB;更关键的是,后续想查 config.features.payment.enabled === true,你只能遍历全部记录再用 JS 过滤——5 万条配置项,游标迭代就耗时 2 秒以上。
- 错误现象:
request.onerror不触发,但onsuccess后查不到数据,或get()返回undefined - 根本原因:V8 序列化大对象时内存溢出,IndexedDB 内部 abort 事务但不抛错
- 兼容性风险:Safari 对单条 value 大小限制更严(实测 > 4MB 就不稳定)
必须提前拍平嵌套字段并建索引
比如你的配置 JSON 里有 {"app": {"theme": "dark", "locale": "zh-CN"}, "features": {"search": {"enabled": true, "maxResults": 50}}},就不能留着嵌套结构。得在写入前提取成顶层字段:
const flattened = {
id: 'main-config',
app_theme: config.app.theme,
app_locale: config.app.locale,
features_search_enabled: config.features.search.enabled,
features_search_maxResults: config.features.search.maxResults
};
- 所有要用于查询的字段(如按
app_locale切换语言包),都得显式调用createIndex() - 复合索引顺序很重要:若常查
app_locale === "zh-CN" && features_search_enabled === true,建索引要用store.createIndex("by-locale-enabled", ["app_locale", "features_search_enabled"]) - 避免用
undefined或NaN作索引值,否则该记录无法被IDBKeyRange定位
超过 10 万条配置项时必须分片
单个 objectStore 存太多记录,打开事务延迟明显(实测 > 15 万条,openCursor() 首次响应超 800ms)。推荐按业务维度切分:
- 把全局配置、用户个性化配置、地区策略配置,分别存在不同
objectStore(如"global_config"、"user_override"、"region_policy") - 对时间敏感的配置(如灰度开关),按日期建库:
"config_2026Q2",旧库只读,新库写入 - 写入时用
transaction.oncomplete分批提交,每批 ≤ 500 条,避免单事务过大被中断
离线场景下最易忽略的持久性陷阱
iOS 是最大变量:Safari 在应用退到后台 30 秒后可能清空 IndexedDB,尤其开启“低电量模式”或存储空间不足时。Android 相对稳定,但 Chrome 自 v120 起对长期闲置 DB 启用自动清理。
- 验证方式:页面关闭前调用
navigator.storage.persisted(),返回false就说明不保证持久 - 补救措施:写入后立即触发一次
get()读取校验,并在pagehide事件中保存最后更新时间戳到localStorage,下次启动时比对是否丢失 - 别依赖“写入即可靠”,IndexedDB 的“持久”是尽力而为,不是强保证











