Tauri 2.x 教程系列 (二):React 组件化与 Tauri 命令系统

📦 本系列完整代码:https://gitee.com/futurelei/rs-tauri-demo

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("用户名不能为空");
    }
    // ... 创建用户逻辑
}

关键点

  1. #[tauri::command] 标记命令函数
  2. 函数的参数自动从 invoke 的参数字典反序列化
  3. 返回值自动序列化为 JSON
  4. 使用 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 开发中最重要的习惯。


上一篇 : Hello Tauri --- 从零搭建第一个桌面应用

下一篇 : 状态管理:Tauri State + Zustand + 事件

相关推荐
guyoung3 小时前
BoxAgnts 工具系统(6)——多 Provider 适配与 Agent 查询循环
rust·agent·ai编程
星栈3 小时前
Rust + Makepad 应用怎么打包发布:Windows、macOS、Linux 全平台交付
前端·rust
MageGojo5 小时前
R-Shell开源项目实战解析:用Rust打造命令行SSH工具,支持连接管理、远程执行、SFTP与MCP
运维·rust·开源项目·命令行工具·ssh客户端·mcp
techdashen5 小时前
Cargo 1.94 开发周期全解析
开发语言·后端·rust
fox_lht7 小时前
15.4.循环和迭代器的性能比较
开发语言·后端·学习·rust
guyoung8 小时前
BoxAgnts 工具系统(5)——WASM 工具开发:从 Hello World 到生产部署
rust·agent·ai编程
星栈8 小时前
写 Makepad Demo 不难,难的是把它写成项目
前端·rust
咸甜适中9 小时前
rust语言学习笔记Trait(十七)Send、Sync(线程间数据所有权)
笔记·学习·rust
javajenius10 小时前
Pixi:用 Rust 重写 Conda 体验的包管理工具
开发语言·其他·rust·conda
laowangpython10 小时前
tokio-rstracing:Rust 可观测性的标准答案
开发语言·后端·其他·rust