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 + 事件

相关推荐
doiito(Do It Together)8 小时前
我用 Rust 写了个 AI 媒体管家:Gliding Horse 赋能 media_agent,目标是让 ComfyUI 工作流彻底自动化
人工智能·架构·rust·媒体
独孤留白13 小时前
从C到Rust:告别 C 的"指针 + 长度"手动模式
前端·rust
咸甜适中13 小时前
rust语言学习笔记(指针十一)Cow<T>(写时克隆)
笔记·学习·rust
doiito14 小时前
【Agent Harness】 给 ComfyUI 装上一个 Rust 大脑:media_agent 架构深度揭秘
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小1 天前
Rust图像处理第11节-故障风 RGB 通道偏移:错位错色制造电子故障
rust·webassembly·图形学
花褪残红青杏小1 天前
Rust图像处理第10节-浮雕/雕刻滤镜:邻域差值生成凹凸效果
rust·webassembly·图形学
Rockbean2 天前
10分钟Solana-性能web3-2.4 Rust 编程基础三:结构体、枚举、错误处理与集合
rust·web3·智能合约
doiito2 天前
【Agent Harness】Gliding Horse 上下文感知与智能压缩:让 Agent 的“注意力”永不偏移
ai·rust·架构设计·系统设计·ai agent