TanStack DB 接入 SQLite 持久化:基于 RxDB SQLite RxStorage 的原生与 WASM 存储实战
TanStack DB 接入 SQLite 持久化基于 RxDB SQLite RxStorage 的原生与 WASM 存储实战【免费下载链接】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/rxdbTanStack DB 是一个把数据保存在内存中的响应式客户端存储它把持久化与同步完全委托给集合collection实现官方tanstack/rxdb-db-collection包让 RxDB 充当这一层。本文聚焦其中 SQLite 方案的完整落地在 Node.js、Electron、React Native、Capacitor 上使用原生 SQLite或在浏览器中用 WebAssembly 版 SQLite 为 TanStack DB 集合提供持久化。读完你将掌握 trial 版与 Premium 版 SQLite 存储的区别、全部sqliteBasics适配器的选型、一个可运行的 Node.js 示例以及它与 TanStack DB 自带 SQLite 持久化包的取舍。为什么在 TanStack DB 之下使用 SQLiteSQLite 在大多数应用运行平台上已经就位Android 与 iOS 系统自带 SQLite 引擎Node.js 22 及更新版本内置了node:sqlite模块Electron 应用可以在主进程中随包分发 SQLite。在移动设备上SQLite 把数据写入文件系统而非浏览器托管存储因此不受 IndexedDB 那套浏览器清理规则约束加上 SQLite 本身经过数十年打磨是存放本地数据的可靠选择。TanStack DB 的集合天然在内存中运行。当它与 RxDB 集合组合后形成两条相互独立的循环详见 TanStack DB RxDB 总览持久化与同步循环由 RxDB 管理每次乐观写入都会经由 RxDB 落入 SQLite 文件应用下次启动时 TanStack DB 集合从 SQLite 文件中恢复初始状态响应式 UI 循环由 TanStack DB 管理来自复制replication或直接 RxDB 代码的变更会通过 RxDB 的变更流自动流入 TanStack DB 集合。数据有意在两个层中各存一份RxDB 负责磁盘上的持久化TanStack DB 保留内存副本用于即时查询。两种版本Trial 版与 Premium 版SQLite 存储存在两个版本两者 API 完全相同维度Trial 版免费Premium 版RxDB Premium导入位置rxdb/plugins/storage-sqlite中的getRxStorageSQLiteTrialrxdb-premium/plugins/storage-sqlite中的getRxStorageSQLite文档上限500 个未删除文档无限制索引不使用索引完整查询支持与索引附件不支持见 rx-attachment支持查询方式抓取整个存储状态在内存中执行查询下推到 SQLite 执行定位仅用于评估与原型生产可用含大量性能优化从源码可以确认 trial 版的限制是硬编码的在 sqlite-storage-instance.ts 中定义了TRIAL_SQLITE_DOCUMENT_LIMIT 500与TRIAL_SQLITE_OPERATION_LIMIT 500达到上限会抛出错误码SQL2/SQL3而在 index.ts 的getRxStorageSQLiteTrial()首次调用时还会打印一条醒目的控制台警告明确提示不应在生产中使用 trial 版。由于两个版本共享同一 API从 trial 切换到 premium 只是改一行 import而不是重写代码——你可以先在 trial 版上把整个 TanStack DB 集成搭建起来再平滑升级。sqliteBasics 适配器统一不同 SQLite 库的差异不同 SQLite 库打开数据库、执行语句的 API 各不相同有的用回调有的用 Promise。RxDB 的 SQLite 存储把这些差异抽象在SQLiteBasics接口之后并为常见 SQLite 包提供了现成实现getSQLiteBasicsNodeNative()面向 Node.js 22 内置的node:sqlite模块getSQLiteBasicsNode()面向sqlite3npm 包getSQLiteBasicsWasm()面向 wa-sqliteSQLite 编译为 WebAssembly用于浏览器getSQLiteBasicsQuickSQLite()面向裸 React Native 项目中的react-native-quick-sqlitegetSQLiteBasicsExpoSQLiteAsync()面向当前 Expo SDK 的expo-sqlitegetSQLiteBasicsExpoSQLite()面向旧版 Expo SDK 的非异步 APIgetSQLiteBasicsWebSQL()面向react-native-sqlite-2getSQLiteBasicsCapacitor()面向 Capacitor 应用中的capacitor-community/sqlitegetSQLiteBasicsTauri()面向 Tauri SQL 插件。注意存储要求 SQLite 版本3.38.0 或更新因为它使用JSON_EXTRACT等 SQLite JSON 函数来查询文档字段。所有适配器的完整代码示例见 SQLite RxStorage 页面。SQLiteBasics 接口的源码级解读接口定义在 sqlite-types.ts每个适配器只需实现五个方法加一个属性export type SQLiteBasicsSQLiteDatabaseType { open: (name: string) PromiseSQLiteDatabaseType; all(db: SQLiteDatabaseType, queryWithParams: SQLiteQueryWithParams): PromiseSQLResultRow[]; run(db: SQLiteDatabaseType, queryWithParams: SQLiteQueryWithParams): Promisevoid; setPragma(db: SQLiteDatabaseType, key: string, value: string): Promisevoid; close(db: SQLiteDatabaseType): Promisevoid; journalMode: WAL | WAL2 | DELETE | TRUNCATE | PERSIST | MEMORY | OFF | ; }其中journalMode指示存储以何种 WALWrite-Ahead Logging模式工作例如 Node 原生适配器使用WAL见 sqlite-basics-helpers.tssqlite3与 Tauri 适配器使用WAL2而 Android 上的 Capacitor 适配器返回空字符串以保留平台默认的 WAL 设置避免重复设置源码注释中引用了 capacitor-community/sqlite 的 issue #258。几个值得注意的实现细节布尔值映射SQLite 本身没有布尔类型。getSQLiteBasicsNodeNative内部通过mapNodeNativeParams()把true/false映射为1/0再绑定参数因为 Node 原生模块无法绑定布尔值wa-sqlite 适配器也通过boolParamsToInt()做了同样的转换见 sqlite-helpers.ts。并发控制wa-sqlite 在并行执行带参数预编译语句时有问题因此getSQLiteBasicsWasm内部维护了一个runQueueWasmSQLite串行队列源码注释指出这是对性能的妥协应在 wa-sqlite 仓库修复Capacitor 适配器则因为不允许并行重开已打开的连接用capacitorOpenCloseQueue把 open/close 调用排队。WebSQL 的 pragma 限制getSQLiteBasicsWebSQL设置 pragma 时捕获23 not authorized错误并忽略因为浏览器中的 WebSQL 不允许设置 pragma。这些适配器对象被getDatabaseConnection()见 sqlite-helpers.ts按数据库名缓存复用连接关闭采用引用计数当所有存储实例都关闭后才真正close底层数据库。实战示例在 Node.js 中把 TanStack DB 持久化到 SQLite以下示例运行在纯 Node.js 22 上使用免费 trial 存储与内置node:sqlite模块无需任何原生编译步骤。1. 安装依赖npm install rxdb rxjs tanstack/react-db tanstack/rxdb-db-collectiontanstack/react-db导出与框架无关的createCollection()因此同样的代码也可以用于 Vue、Solid、Svelte 与 Angular 绑定。2. 在 SQLite 存储上创建 RxDatabaseimport { createRxDatabase } from rxdb/plugins/core; import { getRxStorageSQLiteTrial, getSQLiteBasicsNodeNative } from rxdb/plugins/storage-sqlite; import { DatabaseSync } from node:sqlite; const db await createRxDatabase({ name: exampledb, storage: getRxStorageSQLiteTrial({ sqliteBasics: getSQLiteBasicsNodeNative(DatabaseSync) }) }); 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] } } });从底层看文档并不是按行拆开存储的写入时getSQLiteInsertSQL()/getSQLiteUpdateSQL()见 sqlite-helpers.ts把整份文档JSON.stringify()后存入data列同时以独立列保存id、revision、deleted、lastWriteTime以便复制协议按检查点checkpoint增量拉取查询时则依赖 SQLite 的 JSON 函数在data列内做字段提取与过滤。写入通过sqliteTransaction()串行执行BEGIN/COMMIT/ROLLBACK见 sqlite-helpers.ts遇到并发事务冲突会重试而非直接失败。3. 把 RxCollection 包装成 TanStack DB 集合import { createCollection } from tanstack/react-db; import { rxdbCollectionOptions } from tanstack/rxdb-db-collection; const todosCollection createCollection( rxdbCollectionOptions({ rxCollection: db.todos }) );4. 变更与观察持久化数据// 观察 RxDB 集合确认写入确实进入 SQLite。 db.todos.find().$.subscribe((docs) { // 每次运行脚本计数都会增长 // 因为数据在进程重启后仍然存活。 console.log(todos in SQLite:, docs.length); }); // 在 TanStack DB 集合上执行乐观变更 // 经由 RxDB 持久化到 SQLite 文件。 todosCollection.insert({ id: todo- Date.now(), text: stored in SQLite, completed: false });把脚本连续运行两次第二次启动时会看到第一次写入的 todos 仍然存在因为它们位于 SQLite 数据库中而非内存。在 UI 应用中你可以用useLiveQuery读取这些数据完整示例见 TanStack DB RxDB 总览。5. 上线时切换到 Premium 版import { getRxStorageSQLite, getSQLiteBasicsNodeNative } from rxdb-premium/plugins/storage-sqlite; import { DatabaseSync } from node:sqlite; const storage getRxStorageSQLite({ sqliteBasics: getSQLiteBasicsNodeNative(DatabaseSync) });其余代码完全不变。浏览器中的 SQLitewa-sqlite WASM浏览器没有原生 SQLite但 wa-sqlite 包把 SQLite 编译为 WebAssembly 运行底层可持久化到 IndexedDB 或 OPFSimport { createRxDatabase } from rxdb; import { getRxStorageSQLite, getSQLiteBasicsWasm } from rxdb-premium/plugins/storage-sqlite; import SQLiteESMFactory from wa-sqlite/dist/wa-sqlite-async.mjs; import SQLite from wa-sqlite; const sqliteModule await SQLiteESMFactory(); const sqlite3 SQLite.Factory(sqliteModule); const myRxDatabase await createRxDatabase({ name: exampledb, storage: getRxStorageSQLite({ sqliteBasics: getSQLiteBasicsWasm(sqlite3) }) });需要说明的是SQLite 经 WASM 运行通常比 IndexedDB 或 OPFS 存储更慢因为主线程与 WASM 之间传递数据有额外延迟详见 性能对比。如果浏览器端没有非用 SQLite 不可的硬性要求建议改走 IndexedDB 与 OPFS 持久化指南——你的 TanStack DB 代码无需任何改动。移动端与桌面平台在移动端与桌面端原生 SQLite 是 RxDB进而也是 TanStack DB推荐的首选存储React Native 与 Expo裸项目用getSQLiteBasicsQuickSQLite()搭配react-native-quick-sqliteExpo 应用用expo-sqlite。详见 React Native 数据库指南 与 TanStack DB React Native 文章。Electron在主进程中运行存储避免数据库操作阻塞渲染。详见 Electron 数据库指南 与 TanStack DB Electron 文章。Capacitor使用getSQLiteBasicsCapacitor()搭配capacitor-community/sqlite。详见 Capacitor 数据库指南 与 TanStack DB Capacitor 文章。以 Expo SQLite 为例TanStack DB 的设置如下import { createRxDatabase } from rxdb; import { getRxStorageSQLite, getSQLiteBasicsExpoSQLiteAsync } from rxdb-premium/plugins/storage-sqlite; import * as SQLite from expo-sqlite; const myRxDatabase await createRxDatabase({ name: exampledb, multiInstance: false, storage: getRxStorageSQLite({ sqliteBasics: getSQLiteBasicsExpoSQLiteAsync(SQLite.openDatabaseAsync) }) });随后用rxdbCollectionOptions()包装集合的方式与上面的 Node.js 示例完全一致。注意Expo 应用还有性能明显更好的 Expo Filesystem RxStorage 可作为 SQLite 的替代方案。对比 TanStack DB 自带的 SQLite 持久化TanStack DB 自己也提供了 SQLite 持久化层tanstack/db-sqlite-persistence-core包附带浏览器wa-sqlite、Node.js、Electron、Expo、React Native、Capacitor 的平台适配器。如果目标仅仅是让某个纯本地 TanStack DB 集合在单平台上扛过重启这些包已经够用不需要 RxDB。而 RxDB 的 SQLite 存储覆盖的是更大范围它在你的 store 之下放置一个完整的数据库SQLite 文件成为更大系统的一部分与任意后端复制通过 Sync Engine 支持 GraphQL、CouchDB、Supabase 以及自定义 HTTP 端点并带冲突解决与离线续传加密加密 SQLite 内部存储的数据Schema 迁移应用版本间数据模型变化时平滑升级多标签页支持应用运行于多个浏览器标签页时通过 leader election 协调存储可移植性同一套代码可跑在 IndexedDB、OPFS、localStorage 或 Node.js 文件系统存储 上——切换存储只是改配置不是重写。FAQTanStack DB RxDB 有免费的 SQLite 存储吗有。SQLite RxStorage 的 trial 版随免费 RxDB 核心包一起发布即getRxStorageSQLiteTrial。它限制为 500 个未删除文档仅用于评估与原型带索引与完整查询支持的生产版本属于 RxDB Premium。TanStack DB 能在浏览器里用 SQLite WASM 吗可以。Premium SQLite 存储通过getSQLiteBasicsWasm()适配器支持 wa-sqlite因此你的 TanStack DB 集合可以持久化进 WebAssembly SQLite。不过多数浏览器应用中 IndexedDB 或 OPFS 存储是更快的选择切换时 TanStack DB 代码保持完全一致。TanStack DB 能配合 Expo SQLite 吗能。getSQLiteBasicsExpoSQLiteAsync()适配器把 SQLite 存储接到expo-sqlite模块上TanStack DB 集合通过rxdbCollectionOptions()位于其上层。React Native 数据库指南 列出了各环境推荐的适配器也包括更快的 Expo Filesystem 存储。从 SQLite 切换到其他存储时需要改 TanStack DB 代码吗不需要。存储只在创建数据库时设置一次RxStorage 接口对它之上的所有层隐藏了实现细节。你的 TanStack DB 集合、live query 与变更在 IndexedDB、OPFS、SQLite 或任何其他存储上都保持原样运行。更多阅读TanStack DB RxDB 总览完整集成指南与rxdbCollectionOptions()全部配置项SQLite RxStorage全部适配器的完整代码示例与已知问题附件 BLOB、WITHOUT ROWID、调试日志等IndexedDB 与 OPFS 持久化浏览器端不依赖 SQLite 的持久化方案平台指南React Native、Electron、CapacitorRxDB 快速上手从零开始创建 RxDatabase。【免费下载链接】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),仅供参考