news 2026/9/20 17:21:42

将 TanStack DB 持久化到 IndexedDB 与 OPFS:基于 RxDB 存储层的浏览器持久化配置实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将 TanStack DB 持久化到 IndexedDB 与 OPFS:基于 RxDB 存储层的浏览器持久化配置实战

将 TanStack DB 持久化到 IndexedDB 与 OPFS:基于 RxDB 存储层的浏览器持久化配置实战

【免费下载链接】rxdbThe local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/项目地址: https://gitcode.com/gh_mirrors/rx/rxdb

TanStack DB 默认把所有 collection 数据保存在内存中,页面刷新即丢失;持久化职责由你选择的 collection 实现来承担。官方@tanstack/rxdb-db-collection包把 RxDB(一款存储引擎可插拔的 local-first NoSQL 数据库)置于 TanStack DB collection 之下,于是"如何持久化 TanStack DB"就变成了在 localStorage、IndexedDB 与 OPFS 之间的配置选择。本文以 persist-tanstack-db-indexeddb.md 为主体,完整介绍浏览器端各存储选项的取舍、一套可运行的 localStorage 持久化示例,并展示切换到 IndexedDB 或 OPFS 时只需替换一行storage:配置的做法。读完本文,你将掌握 TanStack DB + RxDB 组合在浏览器端的全部持久化方案与切换技巧。

为什么 TanStack DB 需要一层存储

TanStack DB 是一个内存型响应式客户端 store。实时查询(live queries)与乐观更新(optimistic mutations)都作用在 JavaScript 内存中的数据上,这正是它们"即时"响应的原因。而持久化被明确委托给了 collection 类型:RxDB collection 用真正的数据库来填补这一角色——对 TanStack DB collection 的每次写入都会持久化到对应的 RxCollection,下一次页面加载时,collection 会从磁盘恢复自身状态。

RxDB 本身并不关心"磁盘"具体是什么。所有数据都经由 RxStorage 接口流动,你在创建数据库时选定具体实现。TanStack DB 代码永远不会直接触碰存储层,这正是"切换存储只是改配置、而不是重写代码"的根本原因。

从源码结构看,这一抽象非常直观:例如 getRxStorageLocalstorage 返回一个实现RxStorage接口的RxStorageLocalstorage实例,而createRxDatabase只依赖该接口。存储层的内部细节(如 RxStorageInstanceLocalstorage 把整个状态以字符串化 JSON 存进 localStorage)对上层完全透明。

TanStack DB 在浏览器中的存储选项

对浏览器应用而言,有四类存储值得关注:两类免费、两类属于 RxDB Premium 商业插件。

存储价格基于适用场景
LocalStorage 存储免费localStorage API中小数据集、最简配置
Dexie.js 存储免费IndexedDB无 Premium 权限的较大数据集
IndexedDB 存储 👑付费IndexedDB生产应用、最低延迟、最小构建体积
OPFS 存储 👑付费Origin Private File System大数据集、最快查询

LocalStorage 存储(免费)

基于 localStorage 的存储是起步阶段的推荐默认项:无额外依赖、构建体积小、小数据集读写很快(性能对比见 localstorage-indexeddb-cookies-opfs-sqlite-wasm.md 的 performance-comparison 章节)。其代价是:浏览器通常把 localStorage 限制在每域名约 5 MB,且访问是主线程上的同步操作。对 todo 应用、设置存储或原型来说这完全够用;问题始于数据集增长到几千条文档之后。

源码层面,localStorage 存储把全部状态按 key 分桶存放——docsKey(文档)、attachmentsKey(附件)、changestreamStorageKey(变更流)、indexesKey(索引),见 rx-storage-instance-localstorage.ts。它还借助浏览器原生storage事件实现跨标签页的变更广播(storageEventStream),这也侧面说明它天然适配多标签页场景。

Dexie.js 存储(免费)

Dexie.js 存储通过 Dexie.js 封装库把数据写入 IndexedDB。IndexedDB 没有固定的 5 MB 上限,因此它是"无 Premium 权限、但数据集较大"时的免费选择,也支持使用 Dexie.js 生态的 addons。它的代价包括:控制台会打印一条指向 Premium 存储的提示信息;查询性能落后于 Premium 版 IndexedDB 存储。

IndexedDB 存储 👑

Premium 版 IndexedDB 存储直接构建在原生 IndexedDB 之上,不依赖任何封装库。相比 Dexie.js 存储,它的读写更快、构建体积最多可减少约36%,并运行在类似 SQLite 的 WAL(Write-Ahead Logging)模式下以获得更快写入。在浏览器存储中,它的写入/读取延迟最小、首次页面加载最快,是多数生产应用的默认选择。需要说明的是,这些性能结论以当前仓库文档 rx-storage-indexeddb.md 与 rx-storage-performance.md 的记载为准。

OPFS 存储 👑

Premium 版 OPFS 存储把数据写入Origin Private File System(OPFS)——一项让 Web 应用获得沙箱化、按源隔离的虚拟文件系统并提供字节级文件访问的浏览器 API。由于它操作的是二进制文件而非对象存储,复杂查询下的读取可比 IndexedDB 快最多 4 倍。OPFS 的高速同步方法只存在于 WebWorker 中,因此该存储默认运行在 worker 内,顺带把数据库工作移出主线程。当 TanStack DB collection 持有超过10k条文档时,OPFS 是更优选择;完整数据见 rx-storage-performance.md。

持久化 TanStack DB collection:可运行示例

下面以免费的 localStorage 存储为例,演示完整链路。集成基础(双循环架构、rxdbCollectionOptions配置项等)见 TanStack DB + RxDB 集成概述,此处只保留必要部分。

安装依赖

npm install rxdb rxjs @tanstack/react-db @tanstack/rxdb-db-collection

TanStack DB 还提供 Vue、Solid、Svelte、Angular 的官方绑定。由于rxdbCollectionOptions()面向框架无关的createCollection()API,RxDB collection 与所有官方框架绑定均兼容——例如 React 之外只需把useLiveQuery的导入换成@tanstack/vue-db

创建持久化的 RxDatabase

import { createRxDatabase } from 'rxdb/plugins/core'; import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage'; const db = await createRxDatabase({ name: 'todosdb', // 这一行决定了你的 TanStack DB 数据被持久化到哪里。 storage: getRxStorageLocalstorage() }); await db.addCollections({ todos: { schema: { title: 'todos', version: 0, type: 'object', primaryKey: 'id', properties: { id: { type: 'string', maxLength: 100 }, text: { type: 'string' }, completed: { type: 'boolean' } }, required: ['id', 'text', 'completed'] } } });

schema是标准 RxDB JSON Schema:primaryKey声明主键字段,maxLength限制索引键长度,required强制必填字段。RxDB 会在写入时做模式校验,校验失败会导致写入被拒绝并回滚。

包装成 TanStack DB collection

import { createCollection } from '@tanstack/react-db'; import { rxdbCollectionOptions } from '@tanstack/rxdb-db-collection'; const todosCollection = createCollection( rxdbCollectionOptions({ rxCollection: db.todos }) );

todosCollection从此就是一个标准的 TanStack DB collection:首次加载时从磁盘恢复初始状态,之后持续跟随 RxDB 的变化。rxdbCollectionOptions()还支持若干可选参数:id(collection 唯一标识)、schema(TanStack DB 侧的 Standard Schema 校验,由于 RxDB 已按 JSON Schema 校验,通常仅用于统一错误处理)、startSync(默认true,是否立即开始摄取 RxDB 数据)、syncBatchSize(默认1000,初始加载时每批拉取的文档数,只影响初始加载,后续变更通过 RxDB 变更流逐条流入)。

查询、变更并刷新验证

import { useLiveQuery, eq } from '@tanstack/react-db'; function OpenTodos() { const { data } = useLiveQuery((q) => q .from({ todo: todosCollection }) .where(({ todo }) => eq(todo.completed, false)) ); return <div>{data.length} open todos</div>; } // 写入即时作用于内存,并由 RxDB 持久化。 todosCollection.insert({ id: 'todo-1', text: 'buy milk', completed: false });

写入在内存中即时生效并后台持久化到 RxDB;若持久化失败(如模式校验错误),TanStack DB 会回滚乐观状态。此时刷新页面:collection 会从存储中恢复状态,todo-1依然存在。这正是TanStack DB 持久化的意义所在——内存 store 之所以能扛过刷新,是因为 RxDB 在磁盘上拥有一份持久副本。

从源码看,TanStack DB collection 的写入最终走 RxDB 的批量写方法:insert 对应bulkUpsert()、update 对应incrementalPatch()、delete 对应bulkRemove()(详见 rxdb-collection-for-tanstack-db.md 的 Configuration Options 章节)。

切换到 IndexedDB 或 OPFS

上面的应用代码完全不需要改动,只需替换导入语句和createRxDatabase()里的storage:一行。

使用免费的 Dexie.js 存储(底层即 IndexedDB):

import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; const db = await createRxDatabase({ name: 'todosdb', storage: getRxStorageDexie() });

使用 Premium 版 IndexedDB 存储 👑:

import { getRxStorageIndexedDB } from 'rxdb-premium/plugins/storage-indexeddb'; const db = await createRxDatabase({ name: 'todosdb', storage: getRxStorageIndexedDB() });

使用 Premium 版 OPFS 存储 👑——它通过 worker 存储运行在 WebWorker 内:

import { getRxStorageWorker } from 'rxdb-premium/plugins/storage-worker'; const db = await createRxDatabase({ name: 'todosdb', storage: getRxStorageWorker({ // 该文件必须由你的 Web 服务器静态托管。 workerInput: 'node_modules/rxdb-premium/dist/workers/opfs.worker.js' }) });

如果想省去 worker 部署,rxdb-premium/plugins/storage-opfs导出的getRxStorageOPFSMainThread()可用异步 OPFS API 在主线程运行 OPFS。两种变体的性能差异(worker 对顺序读更快,主线程对批量插入因免去消息序列化开销可能反而更快)对比见 rx-storage-opfs.md 的 using-opfs-in-the-main-thread-instead-of-a-worker 章节,建议针对实际用例分别实测。

需要注意:新的存储从空开始。如果在已有用户数据的应用中切换存储,请先用 storage migration 插件 把既有文档迁移过去。该插件会按批次(batchSize,示例为 500)把旧存储的文档复制进新数据库;它只迁移调用migrateStorage()时新数据库中已定义的 collection,并且会丢弃已删除文档;同时官方明确警告:不要在存储迁移过程中同时修改 schema,应"先迁移存储、再跑 schema 迁移"(见 migration-storage.md)。

TanStack DB 自带持久化方案对比

TanStack DB 也自带浏览器持久化:@tanstack/browser-db-sqlite-persistence适配器通过 wa-sqlite 把 collection 持久化到编译为 WebAssembly 的 SQLite。如果需求仅仅是"collection 在浏览器刷新后存活",该适配器完全够用,无需引入 RxDB。

而 RxDB collection 更适合"持久化只是第一步"的场景:

  • 多种存储:同一套代码路径横跨 localStorage、IndexedDB、OPFS 与原生平台的 SQLite 存储,按环境选择。
  • 复制同步:RxDB 的 Sync Engine 可把持久化数据与任意后端同步,包括 offline-first 行为。
  • 加密:数据落盘前可先 加密本地数据。
  • 多标签页:所有标签页共享同一份持久存储,并通过 leader election 保证复制只在一个标签页运行,而不是每个标签页各自持有一份状态。

FAQ

TanStack DB 默认会把数据持久化到 IndexedDB 吗?

不会。TanStack DB collection 是内存型的,页面刷新即丢失状态。持久化来自 collection 实现:使用RxDB collection时,通过配置对应的 RxStorage,即可持久化到 localStorage、IndexedDB 或 OPFS。

我可以免费把 TanStack DB 持久化到 IndexedDB 吗?

可以。免费的Dexie.js 存储会把 TanStack DB 数据存入 IndexedDB。Premium 版 IndexedDB 存储 👑 更快、构建体积更小,但获得可靠的 IndexedDB 持久化并不需要它。

对 TanStack DB 存储而言,OPFS 比 IndexedDB 快吗?

大数据集上的读取是的。OPFS 存储基于二进制文件工作,读取可比 IndexedDB 快最多 4 倍;而 IndexedDB 存储的首次页面加载更快、构建体积更小。因此当 collection 超过10k条文档时,OPFS 才真正划算。

切换 RxDB 存储会丢数据吗?

不会丢,但数据不会自己搬过去。旧存储保留文档、新存储从空开始。用storage migration 插件执行一次迁移把既有文档复制进新存储,再发布新配置即可。

存储选择会改变我的 TanStack DB 代码吗?

不会。TanStack DB 面向 RxCollection 编程,RxCollection 面向你配置的任意RxStorage编程。查询、变更与实时更新在任何存储上行为一致——切换存储是配置变更,不是重写。

延伸阅读

  • 完整的集成指南见 TanStack DB + RxDB 集成概述。
  • 从 RxDB 快速开始 起步。
  • 在 RxStorage 总览 与 性能对比页 横向比较所有存储。
  • 用同一套代码在原生平台使用 SQLite 存储。
  • 在持久化数据之上叠加 后端同步。

【免费下载链接】rxdbThe local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/项目地址: https://gitcode.com/gh_mirrors/rx/rxdb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 17:19:11

Flipper Zero Tama P1 模拟器实战

Flipper Zero Tama P1 模拟器实战 【免费下载链接】Flipper Playground (and dump) of stuff I make or modify for the Flipper Zero 项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper 一只住进 Flipper Zero 的虚拟宠物 把 Tamagotchi P1 的 ROM 拷进 SD 卡…

作者头像 李华
网站建设 2026/9/20 17:17:24

Docker部署iVentoy:轻松搭建PXE网络批量装机平台

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 17:16:26

从迷宫基线到多智能体电梯群控:深度强化学习调度实现

简介&#xff1a;面向计算机相关专业学生与开发者&#xff0c;这份资源围绕深度强化学习在多智能体电梯群控系统中的应用展开&#xff0c;重点实现目的楼层预约调度算法&#xff0c;适合作为毕设、课设或项目立项演示。包内共30个文件&#xff0c;包含13个Python源码、14个编译…

作者头像 李华
网站建设 2026/9/20 17:13:36

开源AI知识管理工具:团队经验自动传承的瑞士军刀

一个人离职&#xff0c;带着三年的踩坑记录走了&#xff1b;新人刚来&#xff0c;同一批问题在团队里被反复问了一遍又一遍。这个场景我相信每个团队都经历过&#xff0c;但真正把它解决掉的产品没几个。腾讯开源的那个 AI 管理项目第一次打动我的&#xff0c;不是它又接了多少…

作者头像 李华
网站建设 2026/9/20 17:12:14

Atlas 300V 24G部署YOLO实战:从模型转换到推理调优

1. 从“atlas”这个词说起&#xff1a;它到底指什么第一次看到“atlas”这个项目标题&#xff0c;很多人脑子里会蹦出好几个完全不同的东西。做地图的会想到那本地图集&#xff0c;做后端的会想到MongoDB那个托管数据库服务&#xff0c;做AI推理的则会立刻反应过来——这是昇腾…

作者头像 李华