news 2026/9/20 23:04:31

在 Tauri 2 桌面应用中集成 RxDB:基于 SQLite RxStorage 的本地优先数据库实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 Tauri 2 桌面应用中集成 RxDB:基于 SQLite RxStorage 的本地优先数据库实战

在 Tauri 2 桌面应用中集成 RxDB:基于 SQLite 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

本篇技术指南以 examples/tauri 示例项目为主线,完整讲解如何在一个 Tauri 2 桌面应用中接入 RxDB,并通过 SQLite RxStorage 将数据库落盘到系统本地 SQLite。你将在本指南中掌握:Tauri 项目的环境准备与启动流程、Rust 侧 SQL 插件与自定义命令的注册方式、RxDB 数据库与集合的初始化模式,以及如何借助响应式查询实现界面数据自动刷新。

一、示例概览:一个英雄列表桌面应用

该示例实现了一个简单的"英雄列表"(heroes-list)应用:用户可以在界面中输入英雄的名字(name)与颜色(color),数据被持久化到本地 SQLite 数据库中,并实时反映在列表 UI 上。整体技术栈为:

  • Tauri 2 桌面框架(前端基于 Vite + 原生 HTML/JS,后端基于 Rust)
  • SQLite RxStorage(RxDB 的 SQLite 存储层)
  • Tauri SQL 插件(Rust 与前端之间的 SQLite 桥接)

从仓库结构看,示例包含完整的工程骨架:

  • index.html:应用唯一页面,包含英雄列表容器(#heroes-list)与添加英雄的表单
  • src/main.js:前端入口,负责创建 RxDB 实例、订阅查询并绑定插入逻辑
  • src/database.js:RxDB 数据库与集合的创建封装
  • src-tauri:Rust 侧工程,含 lib.rs、tauri.conf.json 与权限声明 capabilities/default.json
  • test/specs/example.e2e.js:基于 WebdriverIO 的端到端测试

二、环境准备与启动步骤

2.1 前置依赖

运行该示例前需要准备:

  1. Node.js 与 npm/yarn 环境(用于前端构建与 RxDB 安装);
  2. Rust 工具链(用于编译 Tauri 的 Rust 后端,示例依赖 tauri 2、tauri-plugin-sql 等 crate,见 Cargo.toml);
  3. Tauri CLI(项目已将其声明为 devDependency,见 package.json);
  4. 系统级的 Tauri 编译依赖(不同操作系统所需的 WebKitGTK / WebView2 等,属于 Tauri 官方要求)。

2.2 启动步骤

官方 README 给出的完整步骤如下(对应 examples/tauri/README.md):

  1. 克隆整个 RxDB 仓库(本仓库即为该仓库的镜像);
  2. 进入项目根目录并安装依赖:cd rxdb && npm install
  3. 进入示例目录:cd examples/tauri
  4. 安装本地化依赖:npm run preinstall && npm install -D
  5. 启动开发模式:npm run tauri dev

其中preinstall脚本会在安装前执行preinstall:rxdb,其实现是:

"preinstall:rxdb": "(cd ../../ && npx yarn@1.22.22 pack ../../ --filename ./examples/tauri/rxdb-local.tgz)"

这段命令将仓库根目录的 RxDB 源码打包成本地 tarballrxdb-local.tgz,随后package.json中的"rxdb": "file:rxdb-local.tgz"会基于这份本地源码包安装 RxDB。这样做保证了示例始终与仓库中的 RxDB 源码保持同步,而不是使用 npm 上可能滞后的发布版本。

提示:仓库中还提供了 reinstall.sh,它会清除node_modulesrxdb-local.tgz,然后重新执行 preinstall 与依赖安装,适合在 RxDB 源码更新后一键重建本地环境。

2.3 Vite 与 Tauri 的端口约定

examples/tauri/vite.config.js 为 Tauri 开发做了针对性配置:

  • clearScreen: false:避免 Vite 清屏掩盖 Rust 编译错误;
  • server.port: 1420, strictPort: true:Tauri 期望固定端口 1420,端口被占用时直接报错而非换端口;
  • hmr.port: 1421:热更新走 WebSocket 端口 1421(仅在设置了TAURI_DEV_HOST时启用 HMR);
  • server.watch.ignored: ["**/src-tauri/**"]:告诉 Vite 忽略对src-tauri目录的监听,避免 Rust 文件的变动触发前端不必要的重载。

与之对应,tauri.conf.json 的build段声明了:

"build": { "beforeDevCommand": "npm run dev", "devUrl": "http://localhost:1420", "beforeBuildCommand": "npm run build", "frontendDist": "../dist" }

即开发模式先启动 Vite 开发服务器(端口 1420)再拉起 Tauri 窗口;构建模式则先执行npm run buildtsc && vite build),产物输出到../dist供 Rust 侧打包。

三、Rust 侧:SQL 插件与自定义命令

3.1 注册 SQL 插件

Tauri 应用需要在 Rust 侧注册 SQL 插件,前端才能通过@tauri-apps/plugin-sql访问 SQLite。见 src-tauri/src/lib.rs:

pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_sql::Builder::new().build()) .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![get_db_suffix]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }

同时 Cargo.toml 中开启了 SQLite 特性:

tauri-plugin-sql = { version = "2", features = ["sqlite"] }

3.2 自定义 Rust 命令:生成数据库后缀

为了让每次运行都能获得独立、全新的数据库实例,示例在 Rust 侧定义了一个命令get_db_suffix,返回当前时间的毫秒时间戳:

#[tauri::command] fn get_db_suffix() -> String { let start = SystemTime::now(); let since_the_epoch = start .duration_since(UNIX_EPOCH) .expect("Time went backwards"); format!("{}", since_the_epoch.as_millis()) }

前端通过invoke("get_db_suffix", {})调用该命令(见 src/main.js),并将返回值拼接到数据库名"heroesdb" + dbSuffix上。这样每次启动都会创建带时间戳后缀的新数据库,避免了旧数据的干扰,这一设计也体现在端到端测试的多次运行场景中。

3.3 权限声明(Capabilities)

Tauri 2 引入了基于 capability 的权限模型。capabilities/default.json 为main窗口声明了:

"permissions": [ "core:default", "opener:default", "sql:default", "sql:allow-execute" ]

其中sql:defaultsql:allow-execute允许前端通过 SQL 插件执行 SQL 语句。缺少这些权限时,前端调用 SQL 插件会被 Tauri 安全层拒绝。

四、前端:初始化 RxDB 与 SQLite RxStorage

4.1 创建 RxDB 实例

examples/tauri/src/database.js 封装了数据库与集合的创建:

import { createRxDatabase, addRxPlugin } from "rxdb"; import { RxDBQueryBuilderPlugin } from "rxdb/plugins/query-builder"; import { RxDBDevModePlugin } from "rxdb/plugins/dev-mode"; addRxPlugin(RxDBQueryBuilderPlugin); addRxPlugin(RxDBDevModePlugin); const heroSchema = { title: "hero schema", description: "describes a simple hero", version: 0, primaryKey: "name", type: "object", properties: { name: { type: "string", maxLength: 100 }, color: { type: "string" }, }, required: ["name", "color"], }; export async function getDatabase(name, storage) { const db = await createRxDatabase({ name, storage }); await db.addCollections({ heroes: { schema: heroSchema } }); return db; }

这里有两个值得注意的设计点:

  • getDatabase(name, storage)将存储层作为参数注入,使得数据库创建逻辑与具体存储实现解耦,测试时可轻松替换为内存存储等实现;
  • 注册了RxDBQueryBuilderPluginRxDBDevModePlugin:前者提供链式查询构建器能力,后者在开发阶段对 schema、查询等进行运行时校验(该插件不应在生产环境使用,它只应在开发模式启用)。

4.2 SQLite RxStorage 的接入

真正的关键在 src/main.js:

import { invoke } from "@tauri-apps/api/core"; import sqlite3 from "@tauri-apps/plugin-sql"; import { getRxStorageSQLiteTrial, getSQLiteBasicsTauri, } from "rxdb/plugins/storage-sqlite"; import { wrappedValidateAjvStorage } from "rxdb/plugins/validate-ajv"; const dbSuffix = await invoke("get_db_suffix", {}); const storage = getRxStorageSQLiteTrial({ sqliteBasics: getSQLiteBasicsTauri(sqlite3), }); const db = await getDatabase( "heroesdb" + dbSuffix, wrappedValidateAjvStorage({ storage: storage }), );

接入链路分为三步:

  1. getSQLiteBasicsTauri(sqlite3):将 Tauri SQL 插件封装为 RxDB SQLite 存储所需的"SQLite 基础能力"(连接、查询、事务等);
  2. getRxStorageSQLiteTrial({ sqliteBasics }):基于上述能力构造 RxDB 的 SQLite 存储实例;
  3. wrappedValidateAjvStorage({ storage }):在其外层叠加 AJV 文档校验,确保写入数据符合 heroSchema 定义。

其中getRxStorageSQLiteTrial名称中的 "Trial" 表明这是 SQLite RxStorage 的试用版实现,正式使用时应参考 SQLite RxStorage 文档 选择合适的变体(例如非试用版或带加密的版本)。命名还揭示了 RxDB 存储层的分层思想:RxStorage 与数据校验是正交的两层,可以任意组合。

五、响应式查询:让 UI 自动跟随数据变化

5.1 订阅查询结果

示例最核心的 RxDB 能力展示是对查询结果的订阅(src/main.js):

db.heroes .find() .sort({ name: "asc" }) .$.subscribe(function (heroes) { if (!heroes) { heroesList.innerHTML = "Loading.."; return; } heroesList.innerHTML = heroes .map((hero) => { return ( "<li>" + '<div class="color-box" style="background:' + hero.color + '"></div>' + '<div class="name" name="' + hero.name + '">' + hero.name + "</div>" + "</li>" ); }) .reduce((pre, cur) => (pre += cur), ""); });

db.heroes.find().sort({ name: "asc" }).$是一个可观察的(Observable)查询流:当集合内数据发生任何增删改时,RxDB 会重新执行查询并向订阅者推送最新结果,前端无需手动管理数据刷新。这正是 RxDB 响应式特性的典型用法——数据层变化自动驱动 UI 渲染。

5.2 插入数据

表单提交通过全局函数addHero完成(src/main.js):

window.addHero = async function () { const name = document.querySelector('input[name="name"]').value; const color = document.querySelector('input[name="color"]').value; const obj = { name: name, color: color }; await db.heroes.insert(obj); };

页面中的按钮通过onclick="addHero();"绑定(见 index.html)。由于 schema 要求namecolor均为必填字段,且name是主键,重复插入同名英雄会被主键冲突规则拦截(开发模式下 DevMode 插件还会给出更详细的校验提示)。

六、端到端测试:验证应用行为

6.1 运行测试

README 指出,运行测试前必须安装 Tauri Driver 及相关系统依赖(tauri-driver是 Tauri 官方的 WebDriver 实现,用于驱动真实窗口执行自动化测试)。

测试脚本定义在 package.json:

"test": "npm run tauri build -- --no-bundle && wdio run wdio.conf.cjs"

即先构建 release 版可执行文件(不打包安装包),再以 WebdriverIO 执行测试。

6.2 测试配置解析

wdio.conf.cjs 的核心配置:

capabilities: [ { maxInstances: 1, 'tauri:options': { application: './src-tauri/target/release/tauri', }, }, ], beforeSession: () => (tauriDriver = spawn( path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'), [], { stdio: [null, process.stdout, process.stderr] } )), afterSession: () => tauriDriver.kill(),

它在每个测试会话开始前自动拉起tauri-driver进程,会话结束后将其终止,被测应用指向 release 构建产物./src-tauri/target/release/tauri

6.3 测试用例

test/specs/example.e2e.js 包含两个用例:

  1. 页面加载成功:断言页面标题文本为RxDB Heroes Tauri
  2. 插入英雄:通过输入框(#input-name#input-color)填写Iron Man/red并点击#input-submit,然后等待列表中只出现一个名为Iron Man的元素(waitForExist超时 20 秒,并断言元素数量为 1)。

第二个用例同时验证了完整链路:前端插入 → RxDB 写入 SQLite → 响应式订阅触发 → DOM 更新出现新条目。由于每次运行都使用新的时间戳后缀数据库,测试的多次执行之间不会互相污染。

七、小结:该示例揭示的 RxDB 桌面端最佳实践

  • 存储层与逻辑层分离getDatabase(name, storage)接受注入的 storage,使数据库代码可复用、可测试;
  • 适配器模式:RxDB 通过getSQLiteBasicsTauri将 Tauri SQL 插件适配为统一的 SQLite 存储接口,因此同一套 RxDB API 可横跨浏览器、Node.js、Electron、Tauri 等不同运行时;
  • 响应式驱动 UI:查询订阅流(.$.subscribe)让数据变更自动映射到界面,这是构建本地优先(local-first)应用的基石;
  • 开发/生产分离:DevMode 插件仅用于开发期校验,生产构建不应包含。

若想在此基础上扩展,可以结合仓库中的 SQLite RxStorage 文档 了解其实现细节与更多配置选项,或在 examples/electron、examples/vite-vanilla-ts 等示例中对比 RxDB 在其他桌面/前端场景下的接入方式。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 23:04:25

RPCS3 补丁管理器完整指南:5 步安装并启用 PS3 游戏补丁

RPCS3 补丁管理器完整指南&#xff1a;5 步安装并启用 PS3 游戏补丁 【免费下载链接】rpcs3 PlayStation 3 emulator and debugger 项目地址: https://gitcode.com/GitHub_Trending/rp/rpcs3 昨晚我用 RPCS3&#xff08;PS3 模拟器&#xff09;跑起一款老游戏&#xff0…

作者头像 李华
网站建设 2026/9/20 23:01:02

腾讯WorkBuddy:大模型赋能的智能开发平台实战解析

1. 腾讯WorkBuddy初体验&#xff1a;大模型应用开发者的新利器作为一名长期深耕大模型应用开发的技术从业者&#xff0c;初次接触腾讯WorkBuddy时的感受可以用"惊艳"来形容。这个集成了大模型能力的智能工作平台&#xff0c;正在悄然改变我们日常的开发协作模式。Wor…

作者头像 李华
网站建设 2026/9/20 22:59:55

SWE-bench Harness 实战:TaoToken 复现 pylint 仓库的 issue

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华