从 Rust 调用前端
本文档包含如何从 Rust 代码与应用程序前端通信的指南。 要了解如何从前端与 Rust 代码通信,请参阅 从前端调用 Rust 。
Tauri 应用程序的 Rust 端可以利用 Tauri 事件系统、 使用通道或直接评估 JavaScript 代码来调用前端。
事件系统
Tauri 提供了一个简单的事件系统,您可以使用它在 Rust 和前端之间进行双向通信。
事件系统是为需要传输少量数据或需要实现多消费者多生产者模式(例如推送通知系统)的情况而设计的。
事件系统并非为低延迟或高吞吐量场景而设计。 请参阅 通道部分 ,了解针对流数据优化的实现。
Tauri 命令和 Tauri 事件之间的主要区别在于,事件没有强类型支持, 事件有效负载始终是 JSON 字符串,这使得它们不适合更大的消息, 并且不支持 功能 系统对事件数据和渠道进行细粒度控制。
AppHandle 和 WebviewWindow 类型实现了事件系统特征 Listener 和 Emitter 。
事件要么是全局的(传递给所有监听器),要么是特定于 webview 的(仅传递给与给定标签匹配的 webview)。
全局事件
要触发全局事件,您可以使用 Emitter#emit 函数:
use tauri::{AppHandle, Emitter};
#[tauri::command]fn download(app: AppHandle, url: String) { app.emit("download-started", &url).unwrap(); for progress in [1, 15, 50, 80, 100] { app.emit("download-progress", progress).unwrap(); } app.emit("download-finished", &url).unwrap();}
Webview 事件
要向特定 webview 注册的监听器触发事件,您可以使用 Emitter#emit_to 函数:
use tauri::{AppHandle, Emitter};
#[tauri::command]fn login(app: AppHandle, user: String, password: String) { let authenticated = user == "tauri-apps" && password == "tauri"; let result = if authenticated { "loggedIn" } else { "invalidCredentials" }; app.emit_to("login", "login-result", result).unwrap();}
也可以通过调用 Emitter#emit_filter 来触发 webview 列表的事件。 以下示例中,我们向主 webview 和文件查看器 webview 发出一个打开文件事件:
use tauri::{AppHandle, Emitter, EventTarget};
#[tauri::command]fn open_file(app: AppHandle, path: std::path::PathBuf) { app.emit_filter("open-file", path, |target| match target { EventTarget::WebviewWindow { label } => label == "main" || label == "file-viewer", _ => false, }).unwrap();}
事件负载
事件负载可以是任何 可序列化 的类型,只要它实现了 Clone 接口。 让我们来进一步改进下载事件的示例,使用一个对象在每个事件中发出更多信息:
use tauri::{AppHandle, Emitter};use serde::Serialize;
#[derive(Clone, Serialize)]#[serde(rename_all = "camelCase")]struct DownloadStarted<'a> { url: &'a str, download_id: usize, content_length: usize,}
#[derive(Clone, Serialize)]#[serde(rename_all = "camelCase")]struct DownloadProgress { download_id: usize, chunk_length: usize,}
#[derive(Clone, Serialize)]#[serde(rename_all = "camelCase")]struct DownloadFinished { download_id: usize,}
#[tauri::command]fn download(app: AppHandle, url: String) { let content_length = 1000; let download_id = 1;
app.emit("download-started", DownloadStarted { url: &url, download_id, content_length }).unwrap();
for chunk_length in [15, 150, 35, 500, 300] { app.emit("download-progress", DownloadProgress { download_id, chunk_length, }).unwrap(); }
app.emit("download-finished", DownloadFinished { download_id }).unwrap();}
监听事件
Tauri 提供 API 来监听 webview 和 Rust 中的事件。
监听前端事件
@tauri-apps/api
NPM 包提供 API 来监听全局事件和特定于 webview 的事件。
-
监听全局事件
import { listen } from '@tauri-apps/api/event';type DownloadStarted = {url: string;downloadId: number;contentLength: number;};listen<DownloadStarted>('download-started', (event) => {console.log(`downloading ${event.payload.contentLength} bytes from ${event.payload.url}`);}); -
监听特定的 webview 事件
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';const appWebview = getCurrentWebviewWindow();appWebview.listen<string>('logged-in', (event) => {localStorage.setItem('session-token', event.payload);});
listen
函数会在应用程序的整个生命周期内保持事件监听器的注册。
要停止监听某个事件,可以使用 listen
函数返回的 unlisten
函数:
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('download-started', (event) => {});unlisten();
此外, Tauri 还提供了一个实用函数,用于精确监听一次事件:
import { once } from '@tauri-apps/api/event';import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
once('ready', (event) => {});
const appWebview = getCurrentWebviewWindow();appWebview.once('ready', () => {});
监听 Rust 上的事件
全局事件和特定于 webview 的事件也会传递给在 Rust 中注册的监听器。
-
监听全局事件
src-tauri/src/lib.rs use tauri::Listener;#[cfg_attr(mobile, tauri::mobile_entry_point)]pub fn run() {tauri::Builder::default().setup(|app| {app.listen("download-started", |event| {if let Ok(payload) = serde_json::from_str::<DownloadStarted>(&event.payload()) {println!("downloading {}", payload.url);}});Ok(())}).run(tauri::generate_context!()).expect("error while running tauri application");} -
监听特定的 webview 事件
src-tauri/src/lib.rs use tauri::{Listener, Manager};#[cfg_attr(mobile, tauri::mobile_entry_point)]pub fn run() {tauri::Builder::default().setup(|app| {let webview = app.get_webview_window("main").unwrap();webview.listen("logged-in", |event| {let session_token = event.data;// save token..});Ok(())}).run(tauri::generate_context!()).expect("error while running tauri application");}
listen
函数会在应用程序的整个生命周期内保持事件监听器的注册。
要停止监听某个事件,可以使用 unlisten
函数:
// 在事件处理程序作用域外取消监听:let event_id = app.listen("download-started", |event| {});app.unlisten(event_id);
// 当满足某些事件条件时取消监听:let handle = app.handle().clone();app.listen("status-changed", |event| { if event.data == "ready" { handle.unlisten(event.id); }});
此外, Tauri 还提供了一个实用函数,用于精确监听一次事件:
app.once("ready", |event| { println!("app is ready");});
在这种情况下,事件监听器在第一次触发后立即取消注册。
通道
事件系统旨在提供简单的双向通信,并可在应用程序中全局使用。 其底层直接执行 JavaScript 代码,因此可能不适合发送大量数据。
通道旨在快速传递有序数据。它们在内部用于流式传输操作, 例如下载进度、子进程输出和 WebSocket 消息。
让我们重写下载命令示例以使用通道而不是事件系统:
use tauri::{AppHandle, ipc::Channel};use serde::Serialize;
#[derive(Clone, Serialize)]#[serde(rename_all = "camelCase", rename_all_fields = "camelCase", tag = "event", content = "data")]enum DownloadEvent<'a> { Started { url: &'a str, download_id: usize, content_length: usize, }, Progress { download_id: usize, chunk_length: usize, }, Finished { download_id: usize, },}
#[tauri::command]fn download(app: AppHandle, url: String, on_event: Channel<DownloadEvent>) { let content_length = 1000; let download_id = 1;
on_event.send(DownloadEvent::Started { url: &url, download_id, content_length, }).unwrap();
for chunk_length in [15, 150, 35, 500, 300] { on_event.send(DownloadEvent::Progress { download_id, chunk_length, }).unwrap(); }
on_event.send(DownloadEvent::Finished { download_id }).unwrap();}
调用下载命令时,您必须创建通道并将其作为参数提供:
import { invoke, Channel } from '@tauri-apps/api/core';
type DownloadEvent = | { event: 'started'; data: { url: string; downloadId: number; contentLength: number; }; } | { event: 'progress'; data: { downloadId: number; chunkLength: number; }; } | { event: 'finished'; data: { downloadId: number; }; };
const onEvent = new Channel<DownloadEvent>();onEvent.onmessage = (message) => { console.log(`got download event ${message.event}`);};
await invoke('download', { url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-schema-generator/schemas/config.schema.json', onEvent,});
执行 JavaScript
要在 webview 上下文中直接执行任何 JavaScript 代码,您可以使用 WebviewWindow#eval
函数:
use tauri::Manager;
tauri::Builder::default() .setup(|app| { let webview = app.get_webview_window("main").unwrap(); webview.eval("console.log('hello from Rust')")?; Ok(()) })
如果要执行的脚本不是那么简单并且必须使用来自 Rust 对象的输入, 我们建议使用 serialize-to-javascript 包。
© 2025 Tauri Contributors. CC-BY / MIT