人人都会AI编程

8.3 事件系统

更新时间:2026-07-11

23.4.1 Rust 端发送事件

插件中的 Rust 代码通常可以访问 AppHandleWindow 实例,通过它们来发射事件。

use tauri::Emitter;

// 向所有窗口广播事件
app_handle.emit("plugin:my-plugin:file-changed", "/path/to/file")?;

// 或者只发送给特定窗口
window.emit("plugin:my-plugin:download-progress", 42)?;

事件名称推荐使用 plugin:<name>:<action> 格式,以避免和其它插件或应用自定义事件冲突。

如果需要发送结构化数据,可以传递一个实现了 serde::Serialize 的类型:

#[derive(serde::Serialize)]
struct Payload {
    file_path: String,
    status: String,
}

app_handle.emit("plugin:my-plugin:status-change", Payload {
    file_path: "/path".into(),
    status: "updated".into(),
})?;

23.4.2 前端监听事件

前端可以通过 Tauri 的 listen 方法订阅事件,并在窗口销毁时自动取消监听。

import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('plugin:my-plugin:file-changed', (event) => {
  console.log('文件变更:', event.payload); // 原始数据
});

// 如果需要在组件卸载时停止监听
// unlisten();

对于 TypeScript 项目,可以定义一个强类型的事件处理:

import { listen } from '@tauri-apps/api/event';

interface StatusChangePayload {
  file_path: string;
  status: string;
}

const unlisten = await listen<StatusChangePayload>(
  'plugin:my-plugin:status-change',
  (event) => {
    console.log(event.payload.file_path); // 类型安全
  }
);

23.4.3 一次性事件与应用级事件

除了 listen,Tauri 也提供 once 用于只监听一次的事件(例如初始化完成通知),以及 emit(在 1.x 中前端事件 API 不同,但概念一致)。插件也可以利用 Window::listen 在 Rust 侧监听前端发回的事件,形成双向通信。


23.4.4 真实示例:下载进度推送

假设我们开发一个文件下载插件,在下载过程中持续推送进度。

Rust 端(插件命令中发送进度)

use tauri::{AppHandle, Emitter};

#[tauri::command]
async fn download_file(app: AppHandle, url: String, save_path: String) -> Result<(), String> {
    for progress in 0..=100 {
        app.emit("plugin:downloader:progress", progress)
            .map_err(|e| e.to_string())?;
        // 模拟下载延迟
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
    Ok(())
}

前端监听并更新进度条

import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/tauri';

async function startDownload() {
  const unlisten = await listen('plugin:downloader:progress', (event) => {
    const percent = event.payload;
    updateProgressBar(percent);
    if (percent === 100) {
      unlisten(); // 下载完成,取消监听
    }
  });

  await invoke('plugin:downloader|download_file', {
    url: 'https://example.com/file.zip',
    savePath: '/tmp/file.zip',
  });
}

23.4.5 注意事项

  • 性能:高频事件(如每秒数百次)可能造成 IPC 拥塞,建议在前端使用节流(throttle)处理,或者在 Rust 端合并频次。
  • 串行化:事件负载需要可序列化,避免传递不可序列化的值(如文件句柄)。
  • 安全性:敏感数据不要用事件广播,应只发送给授权的窗口,并在命令层做权限校验。
  • 生命周期:前端窗口关闭后,监听会自动释放;若在全局作用域注册事件,记得在合适时机调用 unlisten()

通过插件的事件系统,你可以让 Rust 后台与前端界面保持实时、轻量的数据同步,极大地增强了插件与用户交互的能力。