IndexedDB作为NoSQL键值数据库,不支持JOIN和外键,需通过嵌入式结构(小体量高频读)、ID引用(大体量独立更新)或中间关系表(多对多)在应用层实现关联;同时须配合索引、structuredClone及事务控制保障一致性与性能。

IndexedDB 本身是 NoSQL 键值型数据库,不支持传统 SQL 中的 JOIN 或外键约束,但它仍需应对现实业务中常见的实体关联需求——如“用户有多个订单”“文章属于某个分类”“学生选多门课”。处理这些关系不能靠数据库自动维护,而要靠设计模式 + 应用层逻辑来保障一致性与可查询性。
用嵌入式结构表达强一对多关系
当“多”的一方数据体量小、读取频次高、且极少单独更新时,直接嵌入更高效。例如用户与其收货地址:一个用户通常只有 1–3 个常用地址,每次展示用户信息都需连带显示地址。
- 把地址数组作为 user 对象的一个字段存储,如 user.addresses = [{id: "a1", city: "北京", ...}]
- 写入时一次提交整个 user 对象,避免多次事务;查询时无需额外请求,直接解构即可
- 注意限制嵌入深度和单条记录大小(IndexedDB 单条记录建议 ≤ 10MB),避免拖慢序列化/反序列化
用 ID 引用实现松耦合的一对多或多对一
当“多”的一方数据量大、更新频繁、或需独立查询时(如订单、日志、评论),应拆分为独立 objectStore,并通过 ID 关联。
- users store 存用户:{id: "u1", name: "张三", email: "z@x.com"}
- orders store 存订单:{id: "o1", userId: "u1", amount: 299, createdAt: "2026-06-15"}
- 查某用户所有订单:先读 users → 获取 userId → 再用 IDBKeyRange.bound 查询 orders 中所有 userId === "u1" 的记录
- 务必在 orders store 上为 userId 字段建立非唯一索引,否则全表扫描性能极差
用中间关系表建模多对多
学生选课、文章打标签、设备绑定多个用户等场景,必须引入第三张 objectStore 来保存配对关系。
- students store:{id: "s1", name: "李四"}
- courses store:{id: "c1", title: "前端架构"}
- enrollments store(关系表):{studentId: "s1", courseId: "c1", enrolledAt: "2026-06-10"}
- 为 enrollment 建复合索引:如 createIndex("byStudent", ["studentId", "courseId"]),方便快速查某生所有课;再建另一个索引 byCourse 支持反向查询
- 删除学生时,应用层需主动清理其在 enrollments 中的全部记录(IndexedDB 不支持级联)
用结构化克隆预处理复杂嵌套实体
若原始数据含 Date、Map、Set、File 或深层嵌套对象,直接存可能丢失类型或引发错误。structuredClone() 是安全前置步骤。
- 调用 structuredClone(obj) 得到纯净副本,确保不含函数、undefined、Promise、DOM 节点等 IndexedDB 拒绝类型
- 批量写入时,用 Promise.all(requests.map(r => new Promise(...))) 等待所有 add/put 完成,防止事务提前关闭
- 版本升级时若调整了嵌套结构(如把 address 拆出为独立 store),需在 onupgradeneeded 中迁移旧数据

















