在 Tauri 应用中,多窗口之间的通信是一个常见需求。例如,主窗口触发某个操作后,需要实时更新设置窗口的内容;或者多个窗口之间共享同一份数据状态,并同步变化。
Tauri 提供了一套简单的事件系统,让不同窗口之间可以轻松地发送和接收消息,无需额外的 IPC 管道或手动管理窗口引用。
核心 API
- 前端发送事件:使用
@tauri-apps/api提供的emit和emitTo方法 - 前端接收事件:使用
listen监听事件 - 后端(Rust)发送事件:通过
Window实例的emit方法 - 清除监听:
listen返回一个取消监听的函数,避免内存泄漏
实际用法举例
场景:一个“记笔记”应用有两个窗口:主窗口(笔记列表)和编辑窗口(编辑单条笔记)。在编辑窗口中保存笔记后,需要通知主窗口刷新列表。
1. 编辑窗口:保存后发送事件
// 编辑窗口(editor.html 的 JavaScript)
import { emit } from '@tauri-apps/api/event';
async function saveNote(noteData) {
// 调用 Rust 后端保存笔记
await window.__TAURI__.invoke('save_note', { note: noteData });
// 通知所有监听 'note-saved' 事件的窗口
await emit('note-saved', { id: noteData.id });
}
2. 主窗口:监听事件并刷新列表
// 主窗口(main.html 的 JavaScript)
import { listen } from '@tauri-apps/api/event';
async function init() {
// 监听 'note-saved' 事件
const unlisten = await listen('note-saved', (event) => {
console.log('收到事件,笔记ID:', event.payload.id);
// 重新拉取笔记列表
fetchNotes();
});
// 如果窗口被销毁,记得取消监听(例如在框架的 onDestroy 中调用 unlisten())
}
3. 如果只想发送给特定窗口
import { emitTo } from '@tauri-apps/api/event';
// 假设我们提前获取了主窗口的 label(窗口标识符)
await emitTo('main', 'note-saved', { id: noteData.id });
在创建窗口时,Rust 侧可以指定 label:
tauri::WindowBuilder::new(app, "main", /* url */);
前端通过 appWindow.label 可以知道当前窗口的 label。
从 Rust 手动发出事件
有时你希望在 Rust 逻辑中主动通知前端(例如文件监控、后台任务完成),可以直接使用 Window 的 emit 方法:
#[tauri::command]
async fn monitor_folder(window: tauri::Window) {
// 模拟文件变化通知
window.emit("folder-changed", "some/path").unwrap();
}
凡是通过 listen("folder-changed", ...) 监听的前端窗口都会收到。
注意事项
- 事件名称推荐使用带前缀的标识,如
app:note-saved,避免与 Tauri 内部事件冲突。 - payload 类型:支持任何可以被序列化(
Serialize)的数据,对象、字符串、数字均可。 - 内存管理:每次
listen都会建立一个事件监听通道,务必在组件销毁或窗口关闭前调用返回的unlisten函数。 - 跨窗口广播:默认
emit会发送给所有窗口(包括自身),若只想通知其他窗口,可使用emitTo或emitAll的组合。
一句话总结:Tauri 的事件系统让多窗口通信变得像触发和监听自定义事件一样自然,窗口之间完全解耦,代码清晰且易于扩展。