应用端实验室桌面应用
00 / 00

Desktop 项目结构

01MVP Desktop 模板的前端、runtime adapter、Tauri 和后端接入边界

你将学到

  • 桌面模板的四层架构和各自的职责边界
  • 每个目录放什么、不放什么
  • 桌面端如何和后端通信——不直接 import Web 代码
  • src/runtime 为什么是 Tauri 的唯一入口
  • 添加新系统能力的正确顺序

目录总览

products/01mvp/apps/desktop
├── src
│   ├── components        # 模板 UI 组件
│   ├── config            # VITE_DESKTOP_* 校验
│   ├── hooks             # 偏好等客户端状态
│   ├── lib               # Better Auth、oRPC、QueryClient
│   ├── runtime           # Tauri adapter,浏览器预览 fallback
│   ├── App.tsx           # 首屏工作台
│   ├── main.tsx
│   └── styles.css        # Tailwind v4 入口和最小全局样式
├── src-tauri
│   ├── capabilities      # Tauri v2 权限
│   ├── icons             # Bundle icon
│   ├── src               # Rust commands
│   ├── Cargo.toml
│   └── tauri.conf.json
├── .env.example
├── README.md
├── package.json
├── tsconfig.json
└── vite.config.ts

新手最容易犯的错是跨层导入——比如在 React 组件里直接调用 Tauri plugin,或者让桌面端 import Web app 里的模块。下面逐个目录说明边界。

各目录的职责

src/components 只放模板级 UI 组件。不放 API client 调用,不放 Tauri command 细节。组件通过 hooks 获取数据和系统能力。

src/config 集中校验 VITE_DESKTOP_* 环境变量。应用启动时跑一遍,变量缺了或格式错了立刻报错,不会等到运行时才发现。

src/hooks 组合 React state、TanStack Query 和 runtime adapter。比如用户偏好设置的 hook 会调用 runtime adapter 读写本地存储,同时暴露 React 状态给组件。

src/lib 放 Better Auth client、oRPC client 和 TanStack QueryClient 的初始化入口。

src/runtime TypeScript 侧对 Tauri 的唯一入口。每个 adapter 都封装了一个系统能力,并且提供浏览器 fallback——这样用普通 Vite dev server 也能调试 UI,不需要启动 Tauri。

src-tauri/src Rust 侧代码:自定义 commands、托盘逻辑、single instance 控制、原生文件读写。

src-tauri/capabilities Tauri v2 的权限配置。只开放当前需要的权限,不要图省事全部打开。

src-tauri/tauri.conf.json 应用 identity、窗口、CSP、bundle、签名和构建配置。详见配置桌面应用

后端边界

桌面端不 import products/01mvp/apps/web/src/* 的任何模块。业务数据全部通过公开 HTTP 边界访问:

src/lib/api-client.ts   →  ${VITE_DESKTOP_SERVER_URL}/rpc    (oRPC)
src/lib/auth-client.ts  →  ${VITE_DESKTOP_SERVER_URL}/auth   (Better Auth)

两个 client 都使用 credentials: "include",让 WebView 的 cookie 正常携带。

服务端仍然由 Web app 承载:

products/01mvp/apps/web/src/server/hono.ts     # HTTP 入口
products/01mvp/packages/auth/src/index.ts       # 认证
products/01mvp/packages/api/src/router.ts       # 业务 API

桌面端只通过 HTTP 请求和这些服务交互,不共享代码模块。这样两边可以独立部署、独立发版。

Runtime 边界

src/runtime/* 是前端代码接触 Tauri 的唯一通道。每个文件封装一个系统能力,同时提供浏览器 fallback:

// src/runtime/example.ts — 概念示意
export async function readPreference(key: string) {
  if (window.__TAURI__) {
    // 真实 Tauri 调用
    return invoke('read_preference', { key })
  }
  // 浏览器 fallback:用 localStorage 代替
  return localStorage.getItem(key)
}

Rust 侧目前保留的通用能力:

  • 读取和保存本地偏好
  • 打开 app data 目录
  • 托盘打开/退出
  • single instance 控制
  • process/opener plugin skeleton

Updater 的 TypeScript 入口已保留,但默认不注册 Rust updater plugin——配好发布端点、签名公钥和产物策略后才启用。截图、录音、全局快捷键、文件系统等能力应在复制模板后按需添加,不放进默认 scaffold。

添加新功能的顺序

要给桌面端加一个系统能力(比如录屏、全局快捷键),按这个顺序做:

  1. Rust 层 — 在 src-tauri/src/lib.rs 新增 command 或注册 Tauri plugin。
  2. 权限层 — 在 src-tauri/capabilities/default.json 只开放需要的权限。
  3. Adapter 层 — 在 src/runtime/<feature>.ts 封装 TypeScript adapter。
  4. Fallback — 给 adapter 提供浏览器 fallback,至少返回清晰的 unavailable 状态。
  5. 消费层 — 在 src/hooks 或组件里使用 adapter。
  6. 验证 — 确认浏览器 fallback 和参数处理正确。

这个顺序保证排查问题时,能清楚区分前端状态、API 请求、Tauri IPC 和 Rust 原生逻辑。

下一步

了解项目结构后,进入配置桌面应用设置应用身份、环境变量和窗口。

想和其他创造者交流?

这篇文档有问题?