在 Tauri 应用中,前端(WebView)与后端(Rust)之间的通信是单向且受限的——前端无法直接调用 Rust 函数,必须通过 Tauri 提供的 IPC 管道。这套管道本质上支持两种模式:命令 和 事件。理解它们的区别和适用场景,是写出高效 Tauri 应用的关键。
1. 命令模式(Commands)
命令是前端主动调用后端最常见的方式。Rust 函数通过 #[tauri::command] 注解标记,前端使用 invoke() 调用,像调用远程 API 一样传参和接收返回值。
特点:
- 请求-响应:前端发送一个调用,后端执行后返回结果(或被拒绝)。
- 同步调用:前端
await invoke(...)会等待 Rust 函数执行完成。 - 强类型序列化:参数和返回值自动通过 serde 进行序列化/反序列化,传递结构体、字符串、数字等类型都很方便。
示例:
// Rust 后端
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// 前端调用
const greeting = await invoke('greet', { name: 'Tauri' });
console.log(greeting); // "Hello, Tauri!"
适用场景:
- 读取/写入文件
- 执行数据库查询
- 调用系统 API 并同步获取结果
- 任何需要“问一个问题,立刻得到答案”的操作
2. 事件模式(Events)
事件模式用于单向推送消息,特别适合后端主动通知前端的场景。Rust 端可以通过 app_handle.emit() 发送事件,前端用 listen() 订阅并接收。
特点:
- 发布-订阅:后端随时发射事件,前端监听并处理,不需要等待响应。
- 多对多传播:一个事件可以有多个监听者,也可以在前端和后端之间互相发送。
- 异步松耦合:事件发射后不关心谁在监听或是否处理成功。
示例:
// Rust 后端(在某个任务中)
app_handle.emit("progress", 42).unwrap();
// 前端订阅
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('progress', (event) => {
console.log(`进度: ${event.payload}%`);
});
适用场景:
- 长时间运行任务的进度更新(下载、处理大文件)
- 系统托盘菜单点击通知
- 后端主动推送的日志、状态变化
- 避免前端频繁轮询
如何选择?
- 需要返回值 → 命令模式。例如“查询用户数据”“验证密码”,前端期望一个确定的结果。
- 无需返回值,只需通知 → 事件模式。例如“下载进度更新”“应用进入后台”,前端仅需知道状态变化。
- 混合使用:可以用命令启动一个异步任务,并在 Rust 端将任务的阶段性结果通过事件推送给前端。这样保持前端 UI 响应,同时避免阻塞。
Tauri 的这两种模式覆盖了绝大多数桌面应用的通信需求,且都建立在安全沙箱之上——命令需要显式注册,事件载荷也受类型限制,不会破坏前后端的隔离性。掌握它们,基本就掌握了 Tauri 的通信骨架。