人人都会AI编程

8.3 事件系统

更新时间:2026-07-11

核心概念

Tauri 的事件系统分为两类:

  1. 全局事件(App-level events)

作用于整个应用实例,所有窗口都可以收听到。由 Rust 后端通过 AppHandle 触发,前端任意窗口通过 listen() 订阅。

  1. 窗口事件(Window-specific events)

只发送给某一个特定窗口,其他窗口无法收到。由某个窗口对应的 Window 实例触发,通常用于窗口内的特定业务通知。

两类事件都支持带负载(payload)的传输,负载可以是任何可序列化的数据(字符串、数字、JSON 对象等)。


前端使用

前端通过 @tauri-apps/api 提供的 listenemitonce 等方法操作事件。

监听事件(从前端监听后端或其它窗口发送的事件)

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)

后端通过 AppHandleWindow 实例来触发事件。

发送全局事件(所有窗口均可收到)

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 应用从“请求-响应”模式升级到了“发布-订阅”模式,配合命令调用,能覆盖绝大多数通信需求。