- 数据库
- NoSQL
- 嵌入式数据库
- 实时数据库
【免费下载链接】rxdb
The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/
RxDB 与 SignalDB 都是面向 local-first JavaScript 应用的数据库,但两者的定位与成熟度差异明显:SignalDB 主打"信号(signals)驱动"的轻量内存响应式存储,而 RxDB 自 2016 年起将持久化存储、MongoDB 风格查询、多标签协调与后端复制作为一等公民内置。本文以 SignalDB 为对照基线,深入拆解 RxDB 的存储引擎、复制协议、冲突处理与响应式查询实现(附仓库源码路径与配置参数),并给出"SignalDB 做 UI 响应层、RxDB 做持久化与同步"的混合升级路径,帮助你在两者之间做出有依据的技术选型。
一、SignalDB 简史:信号优先的轻量存储
SignalDB 于 2023 年出现,并在 2024 年经由 Maximilian Stoiber 与一个小型开源社区持续演进。它面向已经习惯细粒度响应式原语(signals)的开发者——这类原语被 Solid、Vue 的ref、Preact signals 与 Angular signals 广泛采用。SignalDB 对外暴露一套 MongoDB 风格 API(find、findOne、insert、updateOne),并返回在信号依赖变化时会自动重算的响应式游标(reactive cursors)。
SignalDB 的默认存储是内存:页面刷新即清空,持久化需要通过适配器(localStorage、OPFS、自定义)显式接入。同步同样是可插拔的:SignalDB 提供 sync manager 接口,但期望应用自带传输层、冲突策略与服务端。这种极简设计让包体积很小、API 易于上手,但一旦应用需要离线保障、多设备同步或大数据集,就会把大量工作推给集成方。
二、什么是 RxDB
RxDB 是一款自 2016 年持续开发的本地优先(local-first)、NoSQL JavaScript 数据库,可运行于浏览器、Node.js、Electron、React Native、Deno、Bun 与 Capacitor。数据通过可替换的 RxStorage 层持久化,查询使用 Mongo 风格 selector 引擎,并通过 RxJS 提供观察能力;Sync Engine(复制协议) 提供了经过实战检验的复制协议,覆盖 HTTP、WebSocket、GraphQL、CouchDB、Firestore、NATS 及自定义后端。
与 SignalDB 把存储、复制当作可选附件不同,RxDB 将存储、查询与复制视为一等公民原语。正是这种成熟度,成为团队在业务超出小库能力后迁移到 RxDB 的主要原因。
三、SignalDB 在生产负载下的瓶颈
SignalDB 在它所覆盖的范围内设计良好,但以下缺口会在生产环境中逐渐显现:
- 默认内存存储:数据驻留 RAM,页面重载即丢失(除非接入持久化适配器);大数据集还会与 JS 堆中的其他对象争抢内存。
- 自带同步(BYO Sync):sync manager 是接口而非协议。pull、push、checkpoint、重试与冲突处理都需要自行实现。一旦出现部分离线写入与重连,真实世界的同步远比想象中复杂。
- 存储适配器较少:相比 RxDB 覆盖 IndexedDB、OPFS、Dexie、SQLite、Memory、MongoDB、DenoKV、FoundationDB 等的存储矩阵,SignalDB 的支持后端列表明显更短。
- 多客户端保证有限:SignalDB 未内置多标签协调与 leader election,两个打开的标签页若无自行实现就会产生数据漂移。
- 生态较小:社区插件、示例与长期 issue 历史都更单薄;对需要多年交付的商业应用而言,生态深度至关重要。
- 无 schema 驱动的迁移:SignalDB 集合在运行时是松散类型;RxDB 则强制 JSON Schema 校验,并在 schema 变化时执行带版本号的迁移。
对比数据(截至原文档写作时间 2026-07-30):SignalDB 在 GitHub 上约 673 stars,@signaldb/core近 30 天下载约 6,573 次;RxDB 约 23,296 stars,rxdb包近 30 天下载约 270,494 次。量级差距反映了长期维护与生产部署规模的差异(数据源自原对比文档,仅作参考)。
四、为什么团队选择 RxDB 作为替代
4.1 持久化存储与可交换存储引擎
RxDB 的所有读写都经过 RxStorage 接口抽象。RxStorageDexie等实现类只需实现createStorageInstance(),即可无缝接入数据库实例:
- IndexedDB:覆盖主流浏览器。
- OPFS:借助 Origin Private File System 获得高吞吐浏览器写入。
- Dexie:轻量的 IndexedDB 封装。
- Memory:用于测试与临时状态。
- 服务端侧:SQLite、MongoDB、DenoKV、FoundationDB 等。
从源码看,getRxStorageDexie() 接收一个可选的DexieSettings参数并返回RxStorageDexie实例;在创建存储实例前还会执行参数校验(ensureRxStorageInstanceParamsAreCorrect)。一个值得注意的约束是:Dexie 不支持非必填索引——若 schema 的indexes中出现了不在required列表里的字段,会直接抛出DXE1错误(见 rx-storage-dexie.ts)。这类底层约束正是换引擎前需要了解的差异点。
切换存储引擎通常只是一行代码的改动:查询、复制与响应式层完全保持不变。
4.2 真正的复制协议,而非接口占位
RxDB Sync Engine 在协议层面定义了 pull、push、checkpoint 与冲突语义,应用无需重新发明轮子。其核心位于 replication-protocol 目录:
replicateRxStorageInstance()统一调度下游(downstream,拉取)与上游(upstream,推送)两个方向的复制循环,并维护 checkpoint 队列、firstSyncDone、processed.down/up、resolvedConflicts等事件流(见 src/replication-protocol/index.ts)。RxReplicationState对外暴露received$、sent$、error$、canceled$、active$、conflict$等可观察对象,以及awaitInitialReplication()、awaitInSync()、awaitDocumentPushed()等状态等待 API(见 src/plugins/replication/index.ts)。- 失败请求按
retryTime自动重试;pull 失败会发出RC_PULL错误、push 失败会发出RC_PUSH错误(见 src/plugins/replication/index.ts)。push 响应若未返回数组,会抛出RC_PUSH_NO_AR错误以帮助排查后端实现问题。
插件生态已覆盖 HTTP、WebSocket、GraphQL、CouchDB、Firestore、Supabase、NATS 与 P2P。冲突处理器是按集合显式配置的普通函数,完全由你掌控。
4.3 MongoDB 风格查询 + 真正的响应式
两个库都提供 Mongo 风格 API。RxDB 在此基础上扩展出 可观察查询(observable queries):RxQuery 通过 RxJS 发射结果,并附赠面向 React、Vue、Svelte、Solid、Angular signals 的框架适配层。
从源码看,RxQuery.$是一个惰性初始化的 getter:它订阅集合的eventBulks$(过滤掉 local documents),并在每次变更事件到达时(可配置节流liveQueryUpdateThrottleTime)重新执行查询并发射结果(见 src/rx-query.ts)。这意味着包括其他标签页写入与复制更新在内的任何变更,都会自动触发查询重算。响应式适配层同样简单直接:例如 Vue 适配器把 Observable 订阅映射为shallowRef,并在onScopeDispose中自动退订(见 src/plugins/reactivity-vue/index.ts)。
4.4 开箱即用的多标签与冲突解决
在三个标签页中打开同一个应用:RxDB 自动选举 leader、广播变更、跨标签保持查询同步,无需额外代码。自定义冲突处理器在每个复制周期运行,决定并发编辑如何合并。
机制上,leader-election 插件 基于broadcast-channel实现:waitForLeadership()返回一个 promise,只有当选 leader 的实例才会继续执行复制循环(startReplicationOnLeaderShip,见 src/plugins/replication/index.ts);数据库关闭时会先结束选举再清理广播通道(见 src/plugins/leader-election/index.ts)。
冲突处理则由默认冲突处理器兜底:isEqual使用深比较判断两侧是否一致(可被自定义实现替换为仅比较updatedAt等字段以获得更好性能),resolve默认采用"master 优先"策略,即丢弃本地(fork)状态、采用远端状态(见 src/replication-protocol/default-conflict-handler.ts)。
4.5 自 2016 年积累的成熟生态
RxDB 拥有近十年的版本发布史、付费支持选项以及大规模生产部署。local-first 运动恰恰是围绕 RxDB 这类项目成长起来的,因为长期运行的数据层需要这种稳定性。关于客户端数据所有权这一更宏观的趋势,可参阅 The Future of Local-First Apps 与 offline-first 指南。
五、代码示例:创建集合与响应式查询
下面是一个完整的 RxDB 起步示例——创建数据库、定义带 JSON Schema 的集合、订阅响应式查询,并插入文档触发发射:
import { createRxDatabase, addRxPlugin } from 'rxdb'; import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; const db = await createRxDatabase({ name: 'tasksdb', storage: getRxStorageDexie() // 可替换为 OPFS / Memory / SQLite 等任意 RxStorage }); await db.addCollections({ tasks: { schema: { version: 0, // schema 版本号,升级时用于版本化迁移 primaryKey: 'id', type: 'object', properties: { id: { type: 'string', maxLength: 40 }, title: { type: 'string' }, done: { type: 'boolean' }, updatedAt: { type: 'number' } }, required: ['id', 'title', 'done', 'updatedAt'] } } }); // 响应式查询:只要结果集发生变化就发射, // 包括其他标签页的写入与复制更新。 const openTasks$ = db.tasks .find({ selector: { done: false } }) .sort({ updatedAt: 'desc' }) .$; openTasks$.subscribe(tasks => { console.log('Open tasks:', tasks.length); }); await db.tasks.insert({ id: 't1', title: 'Write SignalDB comparison', done: false, updatedAt: Date.now() });要点提示:
primaryKey必须是 schema 中的必填字符串字段,且带有maxLength(如示例中的 40),这是 RxDB 对主键的硬性约束。.find()返回 RxQuery 对象:.exec()用于一次性读取,.$是可观察对象、每次变更时发射(源码实现见上文 4.3 节)。- 完整 API 面可查阅 RxCollection 与 RxQuery 文档。
六、代码示例:HTTP 复制
HTTP 复制插件 将 RxDB 集合与任何暴露 pull/push 路由的 REST 端点同步。实际上 RxDB 并不存在单独的 "HTTP 插件"——正如 replication-http 文档 所述,复制原语插件(rxdb/plugins/replication)足够简单,可以直接在其上启动 HTTP 复制:
import { replicateRxCollection } from 'rxdb/plugins/replication'; const replicationState = replicateRxCollection({ collection: db.tasks, replicationIdentifier: 'tasks-http-replication', // 用于断点续传与识别远端状态来源 pull: { async handler(checkpoint, batchSize) { const url = `/api/tasks/pull?since=${checkpoint?.updatedAt ?? 0}` + `&limit=${batchSize}`; const response = await fetch(url); const data = await response.json(); return { documents: data.documents, // 需要 { _deleted: boolean } 标记删除 checkpoint: data.checkpoint }; } }, push: { async handler(changeRows) { const response = await fetch('/api/tasks/push', { method: 'POST', body: JSON.stringify(changeRows) }); return await response.json(); // 返回冲突文档列表(如有) } }, live: true, retryTime: 5000 }); replicationState.error$.subscribe(err => console.error(err));replicateRxCollection的完整选项契约定义在 src/types/plugins/replication.d.ts,关键参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
replicationIdentifier | 必填 | 复制标识,用于断点续传;官方建议把服务端 URL 拼入其中 |
deletedField | '_deleted' | 标记文档删除的字段名,可自定义 |
pull.handler(checkpoint, batchSize) | 必填 | 拉取远端增量文档,返回{ documents, checkpoint } |
pull.stream$ | 无 | 后端变更流(live 复制时用于实时推送,如 SSE) |
pull.batchSize | 100 | 单次拉取文档数;少于该数则视为已无更多变更 |
push.handler(changeRows) | 必填 | 推送本地变更,返回冲突文档数组 |
push.batchSize | 100 | 单次推送的本地变更数 |
live | true | false时复制运行一次、同步完成后自动取消 |
retryTime | 5000ms | 失败请求的重试间隔;检测到离线→在线会跳过等待 |
waitForLeadership | true | 多标签场景下等待当前实例成为 leader 后再复制 |
autoStart | true | 设为false时需手动调用replicationState.start() |
同一协议可以扩展到多设备同步、按用户或租户的部分复制(partial replication),以及设备离线后的可恢复传输。
七、混合方案:在 RxDB 持久化之上运行 SignalDB
local-first 社区中逐渐流行一种模式:用 SignalDB 做前端响应层,把存储与同步委托给 RxDB。SignalDB 暴露了持久化适配器接口,因此可以编写一个基于 RxDB 的适配器,实现以下三步:
- 启动时从 RxCollection 读取文档,灌入 SignalDB 集合。
- 将 SignalDB 的写入转发给 RxDB,使其落入持久化存储。
- 订阅 RxDB 的变更流,把远端更新推回 SignalDB,触发信号重新发射。
最终效果:Vue、Solid、React 组件继续消费 SignalDB 友好的信号 API,而 IndexedDB/OPFS 持久化、多标签协调、schema 迁移与后端复制全部由 RxDB 承担。对已经投资 SignalDB、但正在触碰其持久化或同步上限的团队来说,这是一个务实的渐进升级路径。
八、FAQ
RxDB 能集成框架信号(framework signals)吗?
可以。RxDB 内置一套 reactivity 适配器 API,把可观察查询映射为 Vue refs、Angular signals、Solid signals、Svelte stores 与 Preact signals。组件代码通过框架原生原语读取集合,底层由 RxDB 驱动更新。以 Vue 为例,适配器在订阅 Observable 后将其值写入shallowRef并触发triggerRef,同时利用onScopeDispose自动清理订阅(见 src/plugins/reactivity-vue/index.ts)。
SignalDB 可以用 RxDB 持久化吗?
可以。SignalDB 的持久化接口接受自定义适配器:一个基于 RxDB 的适配器把文档存入 RxCollection,从而让 SignalDB 获得 IndexedDB、OPFS、SQLite 或任意 RxStorage 上的持久化能力,外加完整的 RxDB 复制引擎 用于后端同步。这正是第七节描述的混合架构。
两个项目的成熟度如何?
RxDB 自 2016 年持续开发,定期发布版本,并已在浏览器、Node.js、Electron、React Native、Deno、Bun 等环境投入生产。SignalDB 始于 2023 年,仍在扩展其适配器与同步面。对长期运行的应用而言,RxDB 的发布历史与生态是更稳妥的选择。
查询语法有何异同?
两个库都使用 MongoDB 风格 selector。RxDB 的查询返回 RxQuery 对象,提供.exec()一次性读取与.$可观察对象(任何变更——包括其他标签页与复制——都会触发发射);SignalDB 则返回绑定其信号运行时的响应式游标。两者之间的 selector 迁移大多是机械性的。
九、对比总表
| Feature | SignalDB | RxDB |
|---|---|---|
| First release | 2023 | 2016 |
| Default storage | In memory | Durable via RxStorage |
| Storage adapters | localStorage, OPFS, custom | IndexedDB, OPFS, Dexie, Memory, SQLite, MongoDB, DenoKV, FoundationDB, more |
| Query API | MongoDB-style, reactive cursors | MongoDB-style, observable queries |
| Reactivity | Framework signals | RxJS plus framework adapters for React, Vue, Svelte, Solid, Angular |
| Schema and migrations | Loose typing | JSON Schema with versioned migrations |
| Replication | Bring-your-own sync interface | Built-in Sync Engine with HTTP, WebSocket, GraphQL, CouchDB, Firestore, NATS, P2P |
| Conflict resolution | Application-defined | Per-collection conflict handlers |
| Multi-tab support | Manual | Built-in leader election and broadcast |
| Runtimes | Browser, Node.js | Browser, Node.js, Electron, React Native, Deno, Bun, Capacitor |
| Ecosystem age | New | Decade of releases and plugins |
十、选型建议
- 原型、教学或纯内存 UI 状态:SignalDB 的信号风格 API 足够轻快,接入成本最低。
- 需要持久化、离线优先、多标签一致与后端复制的长期业务应用:RxDB 的存储抽象、复制协议与冲突处理是现成的一等公民能力,源码(复制原语、复制协议)与文档均提供了完整可查的实现依据。
- 已深度使用 SignalDB 的团队:不必推倒重来,采用第七节的混合适配器方案,把持久化与同步逐步下沉到 RxDB,风险更低。
更宏观的视角可参考 The Future of Local-First Apps 与 offline-first 指南,理解客户端数据所有权这一趋势如何推动这类选型决策。
- 数据库
- NoSQL
- 嵌入式数据库
- 实时数据库
【免费下载链接】rxdb
The local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/
相关推荐
RxDB 深度解析:local-first 响应式 NoSQL 数据库的架构、复制引擎与快速上手
RxDB 深度解析:local first 响应式 NoSQL 数据库的架构、复制引擎与快速上手 导读 RxDB 是一个面向 JavaScript 应用的 lo
数据库NoSQL嵌入式数据库实时数据库React Table Library性能优化:5个技巧提升大型表格渲染速度
React Table Library性能优化:5个技巧提升大型表格渲染速度 React Table Library 是一款轻量级且功能强大的 React 表格
数据库NoSQL嵌入式数据库实时数据库RxDB 备选方案全景:JavaScript realtime local-first 数据库生态横向对比指南
RxDB 备选方案全景:JavaScript realtime local first 数据库生态横向对比指南 本文基于 RxDB 官方文档中的 Alterna
数据库NoSQL嵌入式数据库实时数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考