资讯详情

RxDB 作为渐进式 Web 应用(PWA)的数据库:离线优先、实时同步与浏览器存储

📅 2026/9/20 16:38:03 | 华诺云谱 👁 阅读
RxDB 作为渐进式 Web 应用(PWA)的数据库:离线优先、实时同步与浏览器存储
RxDB 作为渐进式 Web 应用PWA的数据库离线优先、实时同步与浏览器存储【免费下载链接】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/rxdbProgressive Web AppsPWA兼具 Web 与原生应用的体验优势而数据管理是 PWA 成败的关键一环。本文以 docs-src/docs/articles/progressive-web-app-database.md 为骨架结合 RxDB 仓库源码与配套文档系统讲解如何用 RxDB 为 PWA 构建本地优先local-first的数据层包括 live query 响应式查询、多标签页一致性、多种 RxStorage 存储层选型、与后端/CouchDB/Firestore/WebRTC 的复制同步以及加密、索引、key compression 等进阶能力。读完本文你将掌握在 PWA 中从零接入 RxDB 并落地离线同步的完整实战方案。什么是渐进式 Web 应用PWAPWA 是 Web 开发的未来方向它将 Web 与移动应用的优势合二为一可以像原生应用一样安装到用户主屏幕、离线可用、加载飞快。与混合应用hybrid app不同PWA 在多个平台之间提供一致的用户体验是现代应用的通用选择。PWA 带来了诸多好处免去应用商店的安装与更新流程、降低对网络连接的依赖、优先保证快速加载。借助service worker与智能缓存机制PWA 即使在离线状态下也能让用户访问内容同时 PWA 与设备无关可以无缝适配从桌面到智能手机的各种设备。离线能力的关键在于数据。这也是 RxDB 作为“本地优先数据库”进入 PWA 技术栈的切入点——关于这一范式的整体讨论可参考 离线优先Offline-First 一文。将 RxDB 作为 PWA 的客户端数据库PWA 的心脏是高效的数据管理而 RxDB 正是为此而生。作为一个客户端 NoSQL 数据库RxDB 可以无缝集成进 Web 应用提供实时数据同步与操作能力。它与 PWA 协同工作使本地优先策略成为现实把用户体验提升到全新层次。RxDB 是一个为客户端应用量身打造的响应式、基于 schema 的 NoSQL 数据库其核心特性包括本地优先Local-First方法RxDB 哲学的基石是本地优先PWA 优先在客户端本地完成数据存储与操作。这一范式保证即使离线PWA 依然完全可用——用户随时可以读写数据一旦网络恢复RxDB 会自动补齐数据同步的缺口。从实现看RxDB 把数据放在本地浏览器存储如 LocalStorage、IndexedDB、OPFS中而不是放在服务器上本地数据库成为应用状态持久化变更的“唯一网关”。正如 离线优先 文档指出的在连接被拒绝、中断、间歇或受限的DDIL 环境见 DDIL 数据库中这不仅是一种优化而是唯一可行的架构。可观察查询Observable Queries即 Live Queries可观察查询是 RxDB 动态能力的引擎。借助它PWA 可以实时监听并响应数据变化最终呈现给用户一个即时更新的界面。原文档给出的核心示例await db.heroes.find({ selector: { healthpoints: { $gt: 0 } } }) // 这里的 $ 返回一个 observable每当查询结果集变化时都会 emit .$ .subscribe(aliveHeroes console.dir(aliveHeroes));从源码层面看RxQuery.$的实现位于 src/rx-query.ts#L150-L213它订阅集合的eventBulks$变更事件流过滤掉本地文档local documents的无关变更通过mergeMap(() _ensureEqual())在每次变更后重新执行查询并用shareReplay与distinctUntilChanged保证多个订阅者共享同一份结果、且仅在结果集真正变化时向订阅者推送。换句话说无论变更来自当前页面、另一个浏览器标签页还是复制同步引擎UI 都能自动拿到最新的查询结果——这就是 PWA 中“实时界面”的底层机制。多标签页Multi-Tab支持RxDB 把多标签页能力也纳入自身同一 PWA 在不同标签页/窗口之间保持数据一致用户在应用各个部分间切换时无缝过渡同时把数据冲突降到最低。当 RxDB 在浏览器中运行时数据存储在共享的底层存储如 IndexedDB中所有同源标签页共享同一份数据状态multiInstance机制通过 BroadcastChannel 在标签页间同步变更事件见 src/rx-storage-multiinstance.ts。更深入的多标签页话题复制同步默认只在一个标签页中运行以节省资源这依赖 Leader Election领导选举 插件决定由哪个标签页担任 leader。在 PWA 中集成 RxDBGetting Started将 RxDB 集成进 PWA例如基于 React、Angular、Vue 的工程是很直接的过程安装依赖、选择存储、配置数据库。以下步骤基于 RxDB Quickstart 整理完整可运行。1. 安装依赖npm install rxdb rxjs2. 选择并注册一个 RxStorageRxDB 不是自包含的数据库数据实际存储在某一个RxStorage实现中对应类型接口见 src/types/rx-storage.interface.d.ts。以最简单的 LocalStorage 为例import { createRxDatabase } from rxdb/plugins/core; import { getRxStorageLocalstorage } from rxdb/plugins/storage-localstorage; const db await createRxDatabase({ name: mydatabase, storage: getRxStorageLocalstorage() });3. 开发模式与 Schema 校验开发时建议启用 dev-mode 插件它会给出友好的检查与报错提示import { addRxPlugin } from rxdb/plugins/core; import { RxDBDevModePlugin } from rxdb/plugins/dev-mode; addRxPlugin(RxDBDevModePlugin);生产环境推荐开发环境必需用 AJV 校验包装存储确保写入的每个文档都符合 schemaimport { wrappedValidateAjvStorage } from rxdb/plugins/validate-ajv; storage wrappedValidateAjvStorage({ storage });4. 创建集合Collection与文档操作数据库是 RxDB 的顶层容器负责管理集合与持久化。添加一个带 JSON-Schema 的集合然后进行增删改查await db.addCollections({ todos: { schema: { version: 0, primaryKey: id, // 主键必须设置 maxLength type: object, properties: { id: { type: string, maxLength: 100 }, name: { type: string }, done: { type: boolean }, timestamp: { type: string, format: date-time } }, required: [id, name, done, timestamp] } } }); // 插入 const myDocument await db.todos.insert({ id: todo1, name: Learn RxDB, done: false, timestamp: new Date().toISOString() }); // 查询 const foundDocuments await db.todos.find({ selector: { done: { $eq: false } } }).exec(); // 更新 await foundDocuments[0].patch({ done: true }); // 删除 await foundDocuments[0].remove(); // 观察查询结果UI 永远最新 const observable db.todos.find({ selector: { done: { $eq: false } } }).$; observable.subscribe(notDoneDocs { console.log(Currently have notDoneDocs.length things to do); }); // 观察单个文档字段 myDocument.done$.subscribe(isDone { console.log(done: isDone); });探索不同的 RxStorage 层RxDB 通过可插拔的 RxStorage 接口适应多样化需求你可以根据 PWA 的运行环境与性能要求随时更换底层存储。原文档列出的浏览器侧选项包括LocalStorage RxStorage利用浏览器 LocalStorage API 存储设置最简单、bundle 体积最小适合小数据集与原型。其工厂函数getRxStorageLocalstorage()定义于 src/plugins/storage-localstorage/index.ts#L39-L44。注意 LocalStorage 有约 5MB/域名 的容量限制且操作是同步的会阻塞主线程。IndexedDB RxStorage基于浏览器原生 IndexedDB是浏览器中大数据量的高性能选择premium 版本bundle 更小、启动更快。OPFS RxStorage对接浏览器 Offline-First 持久化系统File System Access API是浏览器内非内存存储中性能最好的方案premium。Memory RxStorage纯内存存储读写极快适合临时数据需求。此外还有更多选择免费且稳定的 Dexie.js 存储IndexedDB 的友好封装实现见 src/plugins/storage-dexie/rx-storage-dexie.ts、适合 Capacitor/Electron/React Native 的 SQLite 存储、Expo Filesystem 存储以及服务端方向的 MongoDB、DenoKV、FoundationDB 等完整清单见 RxStorage 文档。存储层的可组合性是 RxDB 的一大特点各种存储可以像洋葱一样互相包裹。例如要“在浏览器中安全地存储大量数据”可以这样组合校验、压缩、加密与 IndexedDB示例来自 RxStorage 文档import { wrappedValidateAjvStorage } from rxdb/plugins/validate-ajv; import { wrappedKeyCompressionStorage } from rxdb/plugins/key-compression; import { wrappedKeyEncryptionCryptoJsStorage } from rxdb/plugins/encryption-crypto-js; import { getRxStorageIndexedDB } from rxdb-premium/plugins/storage-indexeddb; const db await createRxDatabase({ storage: wrappedValidateAjvStorage({ storage: wrappedKeyCompressionStorage({ storage: wrappedKeyEncryptionCryptoJsStorage({ storage: getRxStorageIndexedDB() }) }) }) });而 Worker、SharedWorker、Sharding、Memory Mapped、Localstorage Meta Optimizer 等包装类存储则用于把查询移出主线程、分片提升写入吞吐、或以内存加速读写。这种灵活性让开发者能针对 PWA 的独特需求定制存储方案。在 PWA 客户端与服务器之间同步数据要实现 PWA 客户端与服务器间的无缝数据同步RxDB 提供多种复制replication方案RxDB 复制算法RxDB Replication AlgorithmRxDB 自研的同步引擎在客户端与服务器之间进行高效、可靠的数据同步详见下文。CouchDB 复制源于 CouchDB 的血统RxDB 可与 CouchDB 服务器平滑复制保证跨设备的数据一致入口见 src/plugins/replication-couchdb/index.ts。Firestore 复制与 Google Firestore实时云托管 NoSQL 数据库同步确保 PWA 不同实例间的数据始终最新入口见 src/plugins/replication-firestore/index.ts。WebRTC 点对点P2P复制客户端之间直接同步、无需中间服务器这种去中心化方案在服务器基础设施有限时尤为宝贵入口见 src/plugins/replication-webrtc/index.ts。同步引擎的设计要点RxDB 同步引擎的核心入口是replicateRxCollection()src/plugins/replication/index.ts其设计目标见 replication.md易于理解采用类似 git 的简单工作方式开发者只需理解三个端点。复杂部分在 RxDB 而非后端冲突处理、离线/在线切换都在 RxDB 内部实现后端可以“很傻”。兼容任意后端无论 PostgreSQL、MongoDB 还是其他协议几乎都能兼容。面向客户端与浏览器优化性能更新与拉取按批次batch分组传输更快、更易压缩。离线优先支持客户端内置冲突处理完全支持离线优先应用。多标签页支持同一应用的多个标签页中同时只有一个运行复制节省客户端与后端资源配合 Leader Election。在文档层面复制类似 git 的 fork/merge客户端先拉取 master 最新状态做本地修改再携带“客户端所认为的 master 状态”推送新状态若 master 也被他人改过就产生冲突冲突在客户端解决。传输层面需要后端实现三个方法pullHandler按 checkpoint 返回之后写入的文档、pushHandler接收客户端写入返回冲突列表、pullStream实时推送 master 写入批次。一个基础用法示例摘自 replication.mdimport { replicateRxCollection } from rxdb/plugins/replication; const replicationState await replicateRxCollection({ collection: myRxCollection, replicationIdentifier: my-rest-replication-to-https://example.com/api/sync, live: true, // 持续实时复制默认 true retryTime: 5 * 1000, // 失败重试间隔默认 5 秒 waitForLeadership: true, // 等待本标签页成为 leader 再复制默认 true autoStart: true, // 自动启动默认 true pull: { async handler(lastCheckpoint, batchSize) { const minTimestamp lastCheckpoint ? lastCheckpoint.updatedAt : 0; const response await fetch( https://example.com/api/sync/?minUpdatedAt${minTimestamp}limit${batchSize} ); const documentsFromRemote await response.json(); return { documents: documentsFromRemote, checkpoint: documentsFromRemote.length 0 ? lastCheckpoint : { id: lastOfArray(documentsFromRemote).id, updatedAt: lastOfArray(documentsFromRemote).updatedAt } }; }, batchSize: 10 }, push: { async handler(docs) { const rawResponse await fetch(https://example.com/api/sync/push, { method: POST, headers: { Accept: application/json, Content-Type: application/json }, body: JSON.stringify({ docs }) }); return rawResponse.json(); // 返回冲突数组无冲突则返回 [] }, batchSize: 5 } });复制的运行分为两种模式Checkpoint 迭代首次复制或重新上线时用 checkpoint 追赶服务器状态pullHandler返回空数组即认为已追平与事件观察连接期间通过pullStream$实时接收事件重连时建议 emitRESYNC让客户端重新迭代。replicationState还提供了awaitInitialReplication()、awaitInSync()、error$、reSync()、pause()/start()、cancel()等管理与观察能力其行为在 test/unit/replication-protocol.test.ts 中有大量测试覆盖。关于“后端数据布局”有一条硬性要求文档必须可按最后写入时间确定性排序通常用updatedAt 主键组成 checkpoint且删除不是物理删除而是把_deleted字段置为true以便复制到其他实例。若后端使用其他删除标记字段可通过deletedField选项映射。RxDB 进阶特性与技巧本地数据加密RxDB 允许 PWA 加密本地数据这对处理用户凭据、金融交易等敏感信息的应用不可或缺详见 加密文档。加密/解密在内部完成当你操作一个 RxDocument 时属性访问与普通数据无异你只需在创建数据库时提供密码并在 schema 的encrypted数组中声明需要加密的字段import { wrappedKeyEncryptionCryptoJsStorage } from rxdb/plugins/encryption-crypto-js; import { getRxStorageLocalstorage } from rxdb/plugins/storage-localstorage; const encryptedStorage wrappedKeyEncryptionCryptoJsStorage({ storage: getRxStorageLocalstorage() }); const db await createRxDatabase({ name: mydatabase, storage: encryptedStorage, password: sudoLetMeIn }); const schema { version: 0, primaryKey: id, type: object, properties: { id: { type: string, maxLength: 100 }, secret: { type: string } }, required: [id], encrypted: [secret] // 声明该字段加密存储 }; await db.addCollections({ myDocuments: { schema } });仓库提供两个加密插件免费的encryption-crypto-js基于 crypto-js 的 AES与 premium 的encryption-web-crypto基于浏览器原生 Web Crypto API。按官方文档说明WebCrypto 方案在写入速度上显著优于 crypto-js。需要注意加密字段不能作为查询操作符无法按加密内容过滤需要先解密再查询且设备被盗时未提供密码的数据无法被读出。索引与性能优化性能优化是 PWA 的重中之重。RxDB 通过索引加速数据检索带来更敏捷的 UI 响应。索引通过在 schema 的indexes属性中声明普通字段索引或使用 自定义索引 实现查询优化器会优先走索引而非全表扫描。关于索引与查询规划器的原理可阅读 rx-query.md 与 query-planner.md对应实现位于 src/query-planner.ts 与 src/custom-index.ts。配合前述 Sharding、Worker 等存储包装还能进一步把查询并行化到多线程。JSON Key CompressionRxDB 提供 key compression基于 JSON Schema 把长属性名压缩成短名称同时保持仍是合法 JSON从而减少存储占用官方文档说明可节省高达 40% 的磁盘空间非常适合数据量大的 PWA。压缩/解压全程内部完成你访问 RxDocument 属性时毫无感知。启用方式是把任意存储用压缩插件包裹并在 schema 中开启keyCompression: trueimport { wrappedKeyCompressionStorage } from rxdb/plugins/key-compression; import { getRxStorageLocalstorage } from rxdb/plugins/storage-localstorage; const storageWithKeyCompression wrappedKeyCompressionStorage({ storage: getRxStorageLocalstorage() }); const db await createRxDatabase({ name: mydatabase, storage: storageWithKeyCompression }); const mySchema { keyCompression: true, // 开启 key compression version: 0, primaryKey: id, type: object, properties: { id: { type: string, maxLength: 100 }, /* ... */ } }; await db.addCollections({ docs: { schema: mySchema } });插件实现位于 src/plugins/key-compression/index.ts并有 test/unit/key-compression.test.ts 覆盖压缩/解压与查询正确性。由于加密对压缩后的数据更高效官方推荐组合顺序为校验在最外层、加密在压缩之内见上文“存储层可组合性”示例。变更流Change Streams与事件处理RxDB 引入变更流让 PWA 能实时响应数据变化驱动界面动态更新。这正是“实时界面”的基础从 RxQuery.$ 到文档字段级的myDocument.done$再到复制引擎的pullStream$、received$/sent$/conflict$等可观察对象见 replication.md 的 RxReplicationState 一节RxDB 把“数据变了”这件事以统一的 RxJS Observable 形式暴露给应用层。对 React 用户还有专门的 react 插件实现见 src/plugins/react可以把查询结果直接映射为组件状态。结论在 Web 应用开发不断演进的今天PWA 持续重塑用户体验。RxDB 作为关键组件无缝集成进 PWA显著增强其能力凭借本地优先策略、可观察查询、多标签页一致性、丰富的复制机制与加密/压缩/索引等进阶特性开发者可以打造响应式、可离线、数据驱动的 PWA。对于希望进一步动手实践的读者从 Quickstart 开始搭建第一个实时应用查看仓库中的 Angular PWA 示例、React 示例、Vue 示例、Svelte 示例 等完整工程见 examples/README.md其中 Angular 示例还演示了多标签页下的 leader election 表现深入 RxDatabase、RxCollection、RxQuery 等核心文档或阅读 RxDB 权衡tradeoffs 了解本地优先方案的边界。拥抱 RxDB你的 PWA 就能把数据掌握在用户手中——离线可用、实时同步、开箱即响应。【免费下载链接】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),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。