Tauri 2.x 教程系列 (二):React 组件化与 Tauri 命令系统
前言
上节课我们搭建了最小 Tauri 应用。本节课将深入 React 组件化开发和 Tauri 的 IPC(进程间通信)系统,实现一个带有完整增删改查的用户管理应用。
本节目标
- ✅ React 组件拆分(Header/Main/Footer)
- ✅ Tauri 命令参数传递与复杂类型返回
- ✅ TypeScript 类型安全封装
- ✅ Rust 端错误处理(Result 模式)
- ✅ 前端三种状态:loading / error / empty
项目结构
02-react-fundamentals/
├── src/
│ ├── components/
│ │ ├── Header.tsx # 页头
│ │ ├── Footer.tsx # 页脚
│ │ ├── UserList.tsx # 用户列表(含 loading/error/empty)
│ │ └── UserCard.tsx # 用户卡片
│ ├── hooks/
│ │ └── useUserApi.ts # Tauri 命令类型安全封装
│ ├── types/
│ │ └── user.ts # TypeScript 类型定义
│ ├── __tests__/ # 前端测试
│ ├── App.tsx # 主组件
│ └── main.tsx
├── src-tauri/
│ ├── src/
│ │ ├── commands.rs # Tauri 命令实现
│ │ ├── models.rs # 数据模型
│ │ ├── lib.rs # 应用入口
│ │ └── main.rs
│ └── Cargo.toml
└── package.json
Rust 后端
数据模型 (models.rs)
rust
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum UserRole { Admin, Editor, Viewer }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u32,
pub name: String,
pub email: String,
pub role: UserRole,
pub is_active: bool,
pub created_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateUserInput {
pub name: String,
pub email: String,
pub role: String,
}
/// API 统一响应包装
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResult<T: Serialize> {
pub success: bool,
pub data: Option<T>,
pub error: Option<String>,
}
Tauri 命令 (commands.rs)
rust
#[tauri::command]
pub fn get_users() -> Vec<User> {
USERS.lock().unwrap().clone()
}
#[tauri::command]
pub fn get_user_by_id(id: u32) -> ApiResult<User> {
let users = USERS.lock().unwrap();
match users.iter().find(|u| u.id == id) {
Some(user) => ApiResult::success(user.clone()),
None => ApiResult::error(format!("用户 {} 不存在", id)),
}
}
#[tauri::command]
pub fn create_user(input: CreateUserInput) -> ApiResult<User> {
// 验证
if input.name.trim().is_empty() {
return ApiResult::error("用户名不能为空");
}
// ... 创建用户逻辑
}
关键点:
#[tauri::command]标记命令函数- 函数的参数自动从 invoke 的参数字典反序列化
- 返回值自动序列化为 JSON
- 使用
ApiResult<T>统一处理成功/失败
命令注册 (lib.rs)
rust
pub fn run() {
commands::init_sample_data();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
commands::get_users,
commands::get_user_by_id,
commands::create_user,
commands::filter_users_by_role,
])
.run(tauri::generate_context!())
.expect("启动失败");
}
前端实现
TypeScript 类型定义
typescript
// src/types/user.ts
export interface User {
id: number;
name: string;
email: string;
role: "admin" | "editor" | "viewer";
is_active: boolean;
created_at: string;
}
export interface ApiResult<T> {
success: boolean;
data?: T;
error?: string;
}
类型安全的 API 封装
typescript
// src/hooks/useUserApi.ts
import { invoke } from "@tauri-apps/api/core";
export const userApi = {
getAll: (): Promise<User[]> => invoke("get_users"),
getById: (id: number): Promise<ApiResult<User>> => invoke("get_user_by_id", { id }),
create: (input: CreateUserRequest): Promise<ApiResult<User>> =>
invoke("create_user", { input }),
};
组件化开发
tsx
// 组件拆分:Header / UserList / UserCard / Footer
// 三种状态处理
function UserList({ users, loading, error }: Props) {
if (loading) return <div className="loading">加载中...</div>;
if (error) return <div className="error">⚠ {error}</div>;
if (users.length === 0) return <div className="empty">暂无数据</div>;
return <div>{users.map(u => <UserCard key={u.id} user={u} />)}</div>;
}
测试
Rust 测试
bash
cd src-tauri && cargo test
覆盖 7 个测试:获取用户、按 ID 查询(存在/不存在)、创建用户(成功/无效角色/空名称)、按角色筛选。
前端测试
bash
npx vitest run
使用 Mock Tauri invoke 测试组件渲染和交互。
运行
bash
npm install
npm run tauri dev
Tauri IPC 核心机制
数据传输流程
前端 (TypeScript) Rust (Serde)
───────────────── ────────────
invoke("get_user_by_id", { id: 1 })
│ │
▼ ▼
JSON 序列化 ────── IPC ──────► JSON 反序列化
│
▼
命令执行
│
JSON 反序列化 ◄────── IPC ────── JSON 序列化
│
▼
类型安全的结果
参数传递规则
| Rust 参数类型 | 前端调用方式 |
|---|---|
String |
{ name: "Alice" } |
u32, i32 |
{ id: 1 } |
struct |
{ input: { name: "...", email: "..." } } |
String (返回值) |
invoke<string>(...) |
总结
本节课我们学会了:
- ✅ React 组件化拆分
- ✅ Tauri 命令参数传递
- ✅ 复杂类型序列化/反序列化
- ✅ 统一错误处理(ApiResult 模式)
- ✅ 前端三种状态处理
- ✅ 前后端测试
关键原则:TypeScript 类型和 Rust 类型要保持一致,这是 Tauri 开发中最重要的习惯。