Skip to content

IndexedDB 定位与 OPFS

基于 Web 现代标准 · 核于 2026-07

速查

  • IndexedDB:浏览器内建的事务型对象数据库——按键索引的对象存储(object store),不是 SQL 表
  • 全异步:操作经由请求对象以事件回调结果,不阻塞主线程;曾有的同步版 API 已从规范移除
  • 能存结构化克隆支持的一切:对象/数组、DateMap/SetBlob/FileArrayBuffer/TypedArray——不需要 JSON 序列化
  • 存不了:函数、DOM 节点(抛 DataCloneError);原型链/属性描述符不保留
  • 严格同源隔离;容量走共享源配额(GB 级,远超 Web Storage 的 ~5 MiB)
  • Worker/Service Worker 全可用WorkerGlobalScope.indexedDB)——重数据处理可整体挪出主线程
  • 原生 API 是出名的啰嗦(事务/版本/游标全靠事件),web.dev 建议配 Promise 包装库 idb
  • OPFS(Origin Private File System)源私有文件系统——对用户不可见、无权限弹窗,入口 navigator.storage.getDirectory()
  • OPFS 与「用户可见的 File System Access API」是两回事:后者带选择器与安全检查且仅 Chromium;OPFS 是 Baseline(2023-03 起全浏览器),写入原地生效、性能高
  • OPFS 王牌:Worker 内的同步访问句柄 createSyncAccessHandle()——read/write/flush/truncate/getSize 全同步,字节级高性能,SQLite-wasm 的标配后端
  • 两者数据都算入源配额、都受驱逐、都要 HTTPS(OPFS 要求安全上下文)
  • 本页只讲定位与模型;事务、游标、句柄的完整 API 归 Web API 章(待产出)

一、IndexedDB:把「数据库」三个字当真

IndexedDB 是浏览器里真正意义上的数据库:事务型、按键索引、面向对象存储。与 Web Storage 的差距不是「容量大一点」,而是整个范式不同:

Web StorageIndexedDB
数据模型扁平键值(仅字符串)对象存储 + 索引(object store / index)
一致性事务(readonly / readwrite)
调用方式同步异步(事件/请求对象)
查询按键取值键、键范围(key range)、索引、游标
容量~5 MiB/源共享源配额(GB 级)
Worker不可用可用

三个模型级要点:

  • 异步是底线:所有读写都不阻塞主线程;规范早期还有一套同步 API,已被移除——「同步用 IndexedDB」这条路根本不存在。
  • 版本化 schema:库结构(有哪些 object store、哪些索引)在版本升级事件里定义,这是它保证结构演进有序的机制。
  • 严格同源:域 A 存的数据域 B 永远拿不到,与其他存储机制的隔离规则一致。

二、能存什么:结构化克隆的边界

IndexedDB 存值用的是结构化克隆算法(structured clone),这是它对 Web Storage 的降维打击——不需要 JSON,也不丢形

能直接存(保形)存不了
原始值、普通对象、数组函数(抛 DataCloneError
DateRegExpDOM 节点(抛 DataCloneError
MapSetgetter/setter、属性描述符(不保留)
BlobFile原型链(类实例存进去、取出来是普通对象)
ArrayBuffer、TypedArray

工程直觉:接口返回的大数组、含 Date 的记录、用户上传的 File、二进制缓冲——直接整个丢进去,取出来还是原来的形状。这正是「结构化数据放 IndexedDB」的底气。

三、容量、Worker 与工程姿势

  • 容量:走 IndexedDB/Cache API/OPFS 共享的源级配额——Chrome 单源可达磁盘 60%,动辄几十 GB(数值与驱逐规则见配额与驱逐)。「几 MB 就发愁」的时代到此为止。
  • Worker 可用indexedDB 在 window、Web Worker、Service Worker 里都有。重查询、批量写入、导入导出可以整体放进 Worker,主线程只收结果。
  • API 人体工学:原生 API 是事件驱动的请求-回调风格,事务、版本升级、游标遍历全要手写样板。web.dev 的建议是配 Promise 包装库 idb(隐藏事务与版本机器,换来 async/await 手感)。方法签名、事务规则、游标细节这些实操内容归 Web API 章(待产出),本叶点到定位为止。

四、OPFS:给源一块私有磁盘

OPFS(Origin Private File System,源私有文件系统)是 File System API 里「私有」的那一半:每个源一棵对用户不可见的文件树,入口一行:

js
// OPFS 根目录(FileSystemDirectoryHandle);需要安全上下文(HTTPS)
const opfsRoot = await navigator.storage.getDirectory();

4.1 与「用户可见的文件系统访问」划清界限

File System Access API(用户可见)OPFS
用户可见性真实文件系统,用户选文件源私有,用户看不见
权限showOpenFilePicker() + 权限弹窗无任何弹窗
写入路径临时文件 + 安全检查(慢)原地写入(快)
兼容性仅 Chromium(web.dev 不推荐)Baseline:2023-03 起全浏览器

两者共用 FileSystemFileHandle/FileSystemDirectoryHandle 这套句柄类型,但定位完全不同:前者是「替用户管他的文件」,OPFS 是「给应用一块高性能私有盘」。

4.2 同步访问句柄:Worker 里的高性能王牌

OPFS 在主线程走异步(getFile() 读、createWritable() 写);真正的杀手锏是仅 Web Worker 可用的同步句柄:

js
// 仅 Worker 内:创建同步访问句柄(创建这一步本身是异步的)
const handle = await fileHandle.createSyncAccessHandle();

// 之后 read / write / flush / truncate / getSize 全是【同步】调用——
// 无 Promise 开销,字节级随机读写
const size = handle.getSize();
handle.write(new TextEncoder().encode("追加内容"), { at: size });
handle.flush(); // 确保落盘
handle.close();

「在 Worker 里同步」听着矛盾,其实正是设计点:同步调用的低开销 + Worker 隔离保证不卡主线程。这让 OPFS 成为把传统文件型软件搬上 Web 的地基——最典型的是 SQLite-wasm 官方把 OPFS 当持久化后端:数据库引擎需要的就是高频、小块、随机的同步 I/O。

4.3 归属与清理

OPFS 不是法外之地:用量算入源配额navigator.storage.estimate() 的 usage 里包含它)、受同一套驱逐规则管、用户「清除站点数据」会连它一起删。文件也不与真实磁盘一一对应——别指望在 Finder/资源管理器里找到它们。

五、选型再落一锤

  • 结构化记录(列表、缓存、含索引查询需求)→ IndexedDB。
  • 文件语义(大二进制、需要字节级随机读写、wasm 数据库文件)→ OPFS。
  • 别过度设计:三五个偏好字段仍然属于 localStorage,为它们开数据库/文件系统是杀鸡用牛刀。
  • 两者互补而非竞争;配合 Worker 都能把重 I/O 全部移出主线程。
  • 两者的数据都在共享源配额与驱逐规则的管辖内——「持久」依旧不等于「保证不丢」。
  • API 细节(事务、游标、句柄方法逐个讲)留给 Web API 章(待产出)。

小结

  • IndexedDB 是异步事务型对象数据库:对象存储 + 索引 + 事务 + 版本化 schema,同步 API 已从规范移除。
  • 结构化克隆让它「存什么都保形」:Date/Map/Blob/ArrayBuffer 直接存;函数与 DOM 节点是边界(DataCloneError)。
  • 容量走共享源配额(GB 级)、Worker 全可用;原生 API 啰嗦,web.dev 推荐 idb 包装。
  • OPFS 是源私有文件系统:无权限弹窗、原地写入、Baseline 全浏览器;Worker 内同步访问句柄是 SQLite-wasm 级性能的来源。
  • 两者都算配额、都可能被驱逐——「能存多大、何时被清」的完整答案在下一页:配额与驱逐