RxDB 的 Dexie.js RxStorage 实战指南:基于 IndexedDB 的浏览器存储、Node.js Polyfill 与后端同步方案
2026/9/21 15:43:49 网站建设 项目流程

RxDB 的 Dexie.js RxStorage 实战指南:基于 IndexedDB 的浏览器存储、Node.js Polyfill 与后端同步方案

【免费下载链接】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

在浏览器中使用 RxDB 时,官方默认推荐使用基于 Dexie.js 与 单元测试 解释其底层原理、局限与取舍。读完本文,你将能在浏览器侧项目、单元测试以及需要 Node 环境执行的场景中正确选用与配置该存储,并判断何时应切换到官方 Premium IndexedDB RxStorage。

Dexie.js RxStorage 是什么

RxDB 是一个本地优先(local-first)数据库,核心设计是可插拔的 RxStorage 抽象层:同一个数据库 API 可以运行在内存、IndexedDB、SQLite、MongoDB 等不同后端之上。Dexie.js RxStorage 就是这一抽象层在浏览器 IndexedDB 上的实现,其导出的工厂函数为getRxStorageDexie(),存储名称为dexie(见 dexie-helper.ts 中的RX_STORAGE_NAME_DEXIE)。

官方对它的定位非常明确:面向浏览器中的小项目与原型,Dexie RxStorage 应作为默认选择(见 rx-storage-dexie.md)。它的优点是接入成本极低、生态成熟(可复用 Dexie.js 庞大的 addon 插件体系),缺点是相对官方 Premium IndexedDB RxStorage 在读写性能与构建体积上存在差距。

Dexie.js vs IndexedDB Storage:何时应该切换

虽然 Dexie.js RxStorage 可以免费使用,但官方建议大多数专业项目在生产环境中切换到Premium IndexedDB RxStorage,原因如下:

  • 更快且构建体积最多减少 36%:Premium 实现绕过了 Dexie 封装层,减少了代码量与间接调用。
  • 读写性能更好:官方有专门的 性能文档 佐证。
  • 附件存储为二进制而非 base64:可减少约 33% 的占用空间,详见 rx-attachment。
  • 不使用 Batched Cursor 或自定义索引:Dexie 方案在这两点上会拖慢查询,相关背景见 slow-indexeddb。
  • 支持非必填索引:这是 Dexie.js 做不到的(见下文“非必填索引限制”一节)。
  • 类 WAL 写入模式:类似 SQLite 的写入方式,写入更快、响应性更好。
  • 支持 Storage Buckets API:可精细管理存储配额,见 rx-storage-indexeddb 文档。

在阅读后续内容时请牢记这一取舍:本文方案适合快速原型与中小型项目,追求极致性能与生产级能力时,应评估 Premium 方案。

快速上手:引入 Dexie Storage 并创建数据库

Dexie RxStorage 的引入非常直接,使用官方rxdb/plugins/corerxdb/plugins/storage-dexie两个入口:

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

在 package.json 中可以看到该插件的包导出配置:dist/types/plugins/storage-dexie/index.d.ts(类型)、dist/cjsdist/esm两份产物,ESM 为默认入口,同时兼容 CommonJS 与浏览器打包器。

从源码结构看,getRxStorageDexie()内部会构造一个RxStorageDexie实例并持有用户传入的DexieSettings(见 rx-storage-dexie.ts)。每个 RxStorage 实例在创建时都会通过ensureRxStorageInstanceParamsAreCorrect()做参数校验(见 rx-storage-dexie.ts)。

非必填索引限制

一个需要特别注意的限制:Dexie.js不支持非必填索引。在RxStorageDexie.createStorageInstance()中,如果 schema 里声明了索引字段但该字段没有出现在required数组中,会直接抛出错误码DXE1(见 rx-storage-dexie.ts)。对应测试见 rx-storage-dexie.test.ts。因此使用本存储时,凡是需要建索引的字段都必须设为必填。

在 Node.js 中运行:用 fake-indexeddb Polyfill IndexedDB

Node.js 本身没有 IndexedDB API,因此 Dexie RxStorage 无法直接运行。官方推荐使用 fake-indexeddb 模块进行 Polyfill,并把实例传给getRxStorageDexie()

import { createRxDatabase } from 'rxdb/plugins/core'; import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; //> npm install fake-indexeddb --save const fakeIndexedDB = require('fake-indexeddb'); const fakeIDBKeyRange = require('fake-indexeddb/lib/FDBKeyRange'); const db = await createRxDatabase({ name: 'exampledb', storage: getRxStorageDexie({ indexedDB: fakeIndexedDB, IDBKeyRange: fakeIDBKeyRange }) });

DexieSettings实际上继承了 Dexie 的DexieOptions,因此indexedDBIDBKeyRange等选项会直接传给 Dexie 构造函数(类型定义见 types/plugins/dexie.d.ts)。

这一能力也直接服务于仓库的测试体系:fake-indexeddb(版本 6.2.5)是 devDependency(见 package.json),单元测试 rx-storage-dexie.test.ts 正是用fake-indexeddbindexedDB作为测试环境;仓库还提供了大量以DEFAULT_STORAGE=dexie运行测试的脚本,例如test:fast:dexietest:node:dexietest:browser:dexie(见 package.json)。这意味着你可以用同样的方式在自己的 Node.js 单元测试或服务端脚本中完整运行 Dexie RxStorage。

使用 Dexie Addons:复用 Dexie.js 插件生态

Dexie.js 拥有自己的插件体系(addons),覆盖加密、复制等场景。使用 Dexie RxStorage 时,可以把这些插件通过addons选项传入:

const db = await createRxDatabase({ name: 'exampledb', storage: getRxStorageDexie({ addons: [ /* Your Dexie.js plugins */ ] }) });

在源码中,addons 同样属于DexieOptions的一部分:getDexieDbWithTables()会以new Dexie(dexieDbName, useSettings)创建底层 Dexie 实例,useSettings是对用户 settings 的浅拷贝并强制设置autoOpen = false(见 dexie-helper.ts),从而保证数据库的打开时机由 RxStorage 内部控制。

还有一个值得关注的设计点:由于 IndexedDB 不适合动态建表,Dexie RxStorage为每个 RxStorage 实例创建一个独立的 Dexie 数据库,命名为rxdb-dexie-<databaseName>--<schema.version>--<collectionName>(见 dexie-helper.ts),每个库内包含docschangesattachments三张表(常量定义见 dexie-helper.ts),并配合引用计数实现连接复用与正确关闭(见 closeDexieDb)。这解释了为什么一个 RxDB 数据库中多个 collection 会对应多个底层 IndexedDB 数据库。

与后端同步的两种路线

本地数据与远端后端保持同步是 RxDB 的核心能力。使用 Dexie RxStorage 时有两种典型路线:

  • Dexie Cloud:官方托管方案,适合快速搭建,开箱即用地提供云端后端与冲突解决。
  • RxDB 原生 replication:对后端、数据流与 冲突处理 拥有完全控制权。

两条路线各有适用场景:追求快速起步可选 Dexie Cloud;需要自主可控与高可定制性则选 RxDB 原生复制。

A. 使用 Dexie Cloud 同步

Dexie Cloud是 Dexie 团队提供的官方 SaaS 方案,开箱即用地提供自动同步、用户管理与冲突解决:

  • 自动同步:本地 IndexedDB 与云端后端自动保持同步。
  • 用户认证:内置用户管理(认证、角色、权限)。
  • 冲突解决:服务端自动处理冲突。
npm install dexie-cloud-addon
import { createRxDatabase } from 'rxdb/plugins/core'; import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; import dexieCloud from 'dexie-cloud-addon'; const storage = getRxStorageDexie({ addons: [dexieCloud], /* * Whenever a new dexie database instance is created, * this method will be called. */ async onCreate(dexieDatabase, dexieDatabaseName) { await dexieDatabase.cloud.configure({ databaseUrl: "https://<yourdatabase>.dexie.cloud", requireAuth: true // optional }); } }); const db = await createRxDatabase({ name: 'mydb', storage });

注意onCreate回调正是DexieSettings中额外扩展的字段(见 types/plugins/dexie.d.ts),其执行时机在new Dexie(...)之后、dexieDb.version(1).stores(...)建表之前(见 dexie-helper.ts),因此非常适合做 Dexie 级配置(如 Cloud 初始化、addon 配置)。

B. 使用 RxDB 原生复制

如果需要最大灵活性,可以选择 RxDB 众多复制插件之一:

  • CouchDB 复制:与 CouchDB 服务器同步。
  • GraphQL 复制:对接任意 GraphQL 端点,适合自定义 schema 或需要 GraphQL 查询能力时。
  • 基于 REST API 的自定义复制:通过 pull/push handler 对接任意 RESTful 后端。

下面以 CouchDB 为例展示完整链路:创建数据库 → 添加 collection → 启动复制。

import { replicateCouchDB } from 'rxdb/plugins/replication-couchdb'; import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; import { createRxDatabase } from 'rxdb/plugins/core'; const db = await createRxDatabase({ name: 'mydb', storage: getRxStorageDexie() }); await db.addCollections({ humans: { schema: { version: 0, type: 'object', primaryKey: 'id', properties: { id: { type: 'string', maxLength: 100 }, name: { type: 'string' }, age: { type: 'number' } }, required: ['id', 'name'] } } }); const replicationState = replicateCouchDB({ replicationIdentifier: 'my-couchdb-replication', collection: db.humans, // The URL to your CouchDB endpoint url: 'http://example.com/db/humans' });

仓库在 test/replication-couchdb.test.ts 中正是以DEFAULT_STORAGE=dexie运行 CouchDB 复制测试(见 package.json),说明该组合是经过完整测试验证的成熟路径。

liveQuery 与 RxDB 的响应式查询

Dexie.js 提供liveQuery特性,可在数据变化时自动刷新查询结果。但 RxDB 本身内置 响应式查询,通常无需启用 Dexie 的 liveQuery:

collection.find().$.subscribe(results => { /* ... 每当结果变化时自动触发 ... */ });

RxDB 会监听变更并自动推送新结果,UI 无需额外插件或手动轮询即可保持同步。这一机制在底层由 Dexie RxStorage 的changeStream()(返回changes$Subject 的可观察流)驱动,写入完成后会发出带 checkpoint 的变更事件(见 rx-storage-instance-dexie.ts 与 bulkWrite 中的事件发布)。

底层实现要点:查询、布尔索引与附件存储

Mango 查询如何在 Dexie 上执行

Dexie RxStorage 的查询逻辑位于 dexie-query.ts:它会根据查询计划(queryPlan)把 RxDB 的 Mango 查询转换成 IndexedDB 的IDBKeyRange(见 getKeyRangeByQueryPlan),然后通过底层 IndexedDB 事务打开复合索引游标逐条读取;如果查询条件未被索引完全满足,还会用getQueryMatcher()在内存中过滤;如果排序未被索引满足,则用getSortComparator()在内存中排序(见 dexieQuery)。游标在queryPlan.sortSatisfiedByIndex且已收集够skip + limit条时提前终止,避免全表扫描。count()则优先走索引的index.count(keyRange)快速路径(见 dexieCount)。

布尔索引与 key-compression 字段转义

IndexedDB 不支持布尔类型索引,因此 Dexie RxStorage 会把布尔值写为字符串'1'/'0'存储,查询范围时再映射回来(见 dexie-helper.ts 的 fromStorageToDexie/fromDexieToStorage 与 dexie-query.ts 的 rangeFieldToBooleanSubstitute)。

另外,若启用了 key-compression 插件,字段名可能以管道符|开头,而 IndexedDB 不允许这类键名,因此写入时会把|替换为__DEXIE_PIPE_SUBSTITUTE),读回时再还原(见 dexie-helper.ts)。以上两套转换均有对应单元测试覆盖(见 rx-storage-dexie.test.ts)。

Store Schema 的自动生成

Dexie 的 store schema 字符串由getDexieStoreSchema()生成(见 dexie-helper.ts):主键排第一位,随后是_deleted+primaryKey复合索引、用户声明的索引、以及_meta.lwt+primaryKey(支撑getChangedDocumentsSince)与_meta.lwt(支撑 cleanup)等内部索引,最终转换为 Dexie 的[field1+field2]复合索引语法。

附件存储

附件的二进制数据存放在独立的attachments表中,以documentId + '||' + attachmentId为键(见 attachmentObjectId),写入与删除在bulkWrite的同一个 Dexie 事务中完成(见 rx-storage-instance-dexie.ts),读取走getAttachmentData()(见 rx-storage-instance-dexie.ts)。这也是前文提到 Premium 方案能以二进制替代 base64 从而节省约 33% 空间的原因之一。

关闭非 Premium 控制台日志

Dexie RxStorage 是免费的开源实现,首次写入时会在控制台输出一条提示信息,告知社区存在更快的 Premium 存储方案。该日志在 bulkWrite 中只输出一次(shownNonPremiumLog标志控制),并且会先检查hasPremiumFlag()。如果你已购买 Premium 并希望关闭该日志,可以调用:

import { setPremiumFlag } from 'rxdb-premium/plugins/shared'; setPremiumFlag();

性能对比

Dexie.js RxStorage 的性能足以应对大多数场景,但其他存储方案(如内存存储、Premium IndexedDB)在特定操作上可能明显更优。下图是官方在 Chrome 浏览器环境对各 RxStorage 的基准对比(纵轴为毫秒,越低越好),可直观看到 Dexie.js 在批量插入等操作上与内存型/IndexedDB 方案的差距:

从图表可以观察到:memoryIndexedDB-memory-mapped在多数操作中耗时极低;dexie.jsBulk insert 500 docs等批量操作中表现尚可,但相比 Premium IndexedDB 方案在读取、并行查询等操作上仍有差距。这正是官方建议生产环境评估 Premium IndexedDB RxStorage 的原因。仓库还提供了性能基准脚本test:performance:dexie(见 package.json),可自行在目标环境复测。

FAQ

Dexie.js 是什么?相比原生 IndexedDB 有什么优势?

Dexie.js 是专为解决原生 IndexedDB 复杂回调式 API 而设计的极简 Promise 封装。它提供直观可链式调用的查询 API、更简单的数据库 schema 定义方式,以及非常健壮的事务管理。但 Dexie 缺少文档型 NoSQL 数据库的高级查询能力(如深层嵌套 JSON 查询、完整的 MongoDB 风格 selector)。而 RxDB 把 Dexie 作为底层存储引擎使用,弥补了这些不足:RxDB 提供完全响应式的高级 NoSQL 查询引擎、跨平台离线复制协议,以及 Dexie 本身不具备的内置字段加密能力。

总结

Dexie.js RxStorage 是浏览器端接入 RxDB 最便捷的存储方案:一条工厂函数即可建库,天然支持响应式查询、附件与复制协议,可通过fake-indexeddb在 Node.js 中运行测试,还能复用 Dexie addon 生态(包括 Dexie Cloud)。理解它的底层实现——每实例一库的表结构、布尔索引与 key-compression 转义、基于查询计划的 IndexedDB 游标扫描——有助于你在真实项目中预判行为与排查问题。同时务必记住两个关键边界:索引字段必须为必填,且在小项目与原型之外,生产环境应评估官方 Premium IndexedDB RxStorage 以获得更好的性能与更小的体积。

【免费下载链接】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),仅供参考

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

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

立即咨询