核心概念
Tauri 的事件系统分为两类:
- 全局事件(App-level events)
作用于整个应用实例,所有窗口都可以收听到。由 Rust 后端通过 AppHandle 触发,前端任意窗口通过 listen() 订阅。
- 窗口事件(Window-specific events)
只发送给某一个特定窗口,其他窗口无法收到。由某个窗口对应的 Window 实例触发,通常用于窗口内的特定业务通知。
两类事件都支持带负载(payload)的传输,负载可以是任何可序列化的数据(字符串、数字、JSON 对象等)。
前端使用
前端通过 @tauri-apps/api 提供的 listen、emit、once 等方法操作事件。
监听事件(从前端监听后端或其它窗口发送的事件)
import { listen } from '@tauri-apps/api/event';
// 开始监听一个名为 'update-status' 的事件
const unlisten = await listen('update-status', (event) => {
console.log('收到负载:', event.payload);
});
// 当你不再需要监听时,取消订阅
unlisten();
listen返回一个释放函数,适合在组件销毁时调用,避免内存泄漏。once用于只监听一次后自动取消。
发送事件到后端(前端触发,后端监听)
import { emit } from '@tauri-apps/api/event';
await emit('frontend-action', { message: '用户点击了按钮' });
后端使用(Rust)
后端通过 AppHandle 或 Window 实例来触发事件。
发送全局事件(所有窗口均可收到)
use tauri::Manager;
#[tauri::command]
fn do_something(app: tauri::AppHandle) {
// 发送一个全局事件,负载为字符串
app.emit_all("update-status", "后端处理完成").unwrap();
}
发送窗口事件(仅目标窗口收到)
// 假设已经拿到了某个窗口的 Window 实例
window.emit("window-specific", serde_json::json!({"progress": 80})).unwrap();
监听来自前端的事件
后端也可以监听前端通过 emit 发送的事件:
let app_handle = app.handle();
app_handle.listen_global("frontend-action", |event| {
println!("来自前端的消息: {:?}", event.payload());
});
实际应用场景
- 全局状态同步:后端文件扫描完毕 -> 触发全局事件 -> 所有窗口更新 UI。
- 任务进度通知:Rust 里进行耗时处理时,持续 emit 进度事件,前端实时渲染进度条。
- 窗口间通信:窗口 A 发起事件,窗口 B 监听响应,无需绕道后端(前端也可以直接 emit 全局事件,其他窗口的监听器会收到)。
- 解耦业务逻辑:比如用户登录成功后 emit
user-login事件,多个窗口的监听器各自执行刷新购物车、加载消息等操作,而不用串行调用多个命令。
注意事项
- 负载序列化:事件负载需要是可序列化的。Rust 侧常用
serde_json::Value或自定义结构体(需派生Serialize)。 - 性能:事件总线基于内存通道,毫秒级延迟,适合中小型数据传递。不要用事件传输超大文件(应改用命令 + 文件路径)。
- 命名管理:建议用有意义的命名空间,例如
app:update-status避免冲突。 - 取消监听:在前端及时释放监听器(尤其在组件卸载时),否则可能导致内存泄漏或重复处理。
事件系统让 Tauri 应用从“请求-响应”模式升级到了“发布-订阅”模式,配合命令调用,能覆盖绝大多数通信需求。