从 SignalDB 迁移到 RxDB:为 Local-First JavaScript 应用选择可持久化数据库与复制引擎
2026/9/20 15:48:12 网站建设 项目流程
  • 数据库
  • 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/

项目地址:https://gitcode.com/gh_mirrors/rx/rxdb
点击查看免费下载

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(findfindOneinsertupdateOne),并返回在信号依赖变化时会自动重算的响应式游标(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 队列、firstSyncDoneprocessed.down/upresolvedConflicts等事件流(见 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.batchSize100单次拉取文档数;少于该数则视为已无更多变更
push.handler(changeRows)必填推送本地变更,返回冲突文档数组
push.batchSize100单次推送的本地变更数
livetruefalse时复制运行一次、同步完成后自动取消
retryTime5000ms失败请求的重试间隔;检测到离线→在线会跳过等待
waitForLeadershiptrue多标签场景下等待当前实例成为 leader 后再复制
autoStarttrue设为false时需手动调用replicationState.start()

同一协议可以扩展到多设备同步、按用户或租户的部分复制(partial replication),以及设备离线后的可恢复传输。

七、混合方案:在 RxDB 持久化之上运行 SignalDB

local-first 社区中逐渐流行一种模式:用 SignalDB 做前端响应层,把存储与同步委托给 RxDB。SignalDB 暴露了持久化适配器接口,因此可以编写一个基于 RxDB 的适配器,实现以下三步:

  1. 启动时从 RxCollection 读取文档,灌入 SignalDB 集合。
  2. 将 SignalDB 的写入转发给 RxDB,使其落入持久化存储。
  3. 订阅 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 迁移大多是机械性的。

九、对比总表

FeatureSignalDBRxDB
First release20232016
Default storageIn memoryDurable via RxStorage
Storage adapterslocalStorage, OPFS, customIndexedDB, OPFS, Dexie, Memory, SQLite, MongoDB, DenoKV, FoundationDB, more
Query APIMongoDB-style, reactive cursorsMongoDB-style, observable queries
ReactivityFramework signalsRxJS plus framework adapters for React, Vue, Svelte, Solid, Angular
Schema and migrationsLoose typingJSON Schema with versioned migrations
ReplicationBring-your-own sync interfaceBuilt-in Sync Engine with HTTP, WebSocket, GraphQL, CouchDB, Firestore, NATS, P2P
Conflict resolutionApplication-definedPer-collection conflict handlers
Multi-tab supportManualBuilt-in leader election and broadcast
RuntimesBrowser, Node.jsBrowser, Node.js, Electron, React Native, Deno, Bun, Capacitor
Ecosystem ageNewDecade 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/

项目地址:https://gitcode.com/gh_mirrors/rx/rxdb
点击查看免费下载

相关推荐

上一篇:netboot.xyz技术架构详解:从模板到启动镜像
下一篇:WinUtil:告别繁琐设置,一键实现Windows系统优化与软件管理

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询