在 Tauri 应用中,Rust 主进程是应用的后台核心,负责管理窗口、处理系统调用、运行后台任务,并与前端通过 IPC 通信。理解它的入口与几个关键概念,是写好 Tauri 应用的基础。
1. 主程序入口:main.rs
Tauri 应用的 Rust 入口通常在 src-tauri/src/main.rs(若使用 Tauri CLI 初始化项目,默认如此)。最基本的骨架如下:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("启动应用时出错");
}
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]:在 Windows 发布模式下不显示控制台窗口。tauri::Builder::default():创建一个默认的应用构建器。.run(tauri::generate_context!()):加载tauri.conf.json配置,启动 WebView 窗口并运行事件循环。调用后主线程会被阻塞,直到应用退出。
这已经是一个可以运行的 Tauri 应用——它会根据 tauri.conf.json 中的 build.devPath 或 build.distDir 加载前端页面。
2. 命令(Commands):前端调用的 Rust 函数
前端无法直接访问系统,必须通过“命令”来调用 Rust 函数。定义一个命令非常简单:
#[tauri::command]
fn greet(name: &str) -> String {
format!("你好,{}!", name)
}
然后在 main 里注册:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("启动应用时出错");
}
前端通过 @tauri-apps/api 调用:
import { invoke } from '@tauri-apps/api/tauri';
const msg = await invoke('greet', { name: 'Tauri' });
核心要点:
- 命令参数和返回值必须实现
serde::Serialize/Deserialize。 - 参数名会与前端传递的 JSON 字段自动匹配。
- 命令中可以使用
tauri::Window,tauri::State等特殊类型来访问应用状态或窗口实例。
3. 应用状态(State):跨命令共享数据
Tauri 用 Rust 的“状态管理”模式来共享资源,例如数据库连接、配置对象等。在 Builder 上注册状态:
struct AppConfig {
app_name: String,
}
fn main() {
tauri::Builder::default()
.manage(AppConfig { app_name: "MyApp".into() })
.invoke_handler(tauri::generate_handler![get_app_name])
.run(tauri::generate_context!())
.unwrap();
}
#[tauri::command]
fn get_app_name(config: tauri::State<AppConfig>) -> String {
config.app_name.clone()
}
State<T> 会自动注入,多个命令可以共享同一个 T 的实例(注意 T 必须 Send + Sync)。对于需要互斥访问的数据,通常内部使用 std::sync::Mutex 或 tokio::sync::Mutex。
4. 窗口与菜单管理
Tauri 的 Builder 允许你在启动时创建窗口,也可以在运行时动态创建。配置窗口最直接的方式在 tauri.conf.json 的 windows 数组中,但也可以通过 Rust 代码配合 tauri::WindowBuilder:
use tauri::Manager;
fn main() {
tauri::Builder::default()
.setup(|app| {
let _window = tauri::WindowBuilder::new(
app,
"label",
tauri::WindowUrl::App("index.html".into()),
)
.title("新窗口")
.inner_size(800.0, 600.0)
.build()?;
Ok(())
})
.run(tauri::generate_context!())
.unwrap();
}
app.get_window("label")可获取已存在的窗口。- 菜单、系统托盘也通过 Rust 端创建,并可用
on_menu_event等闭包处理点击。
5. 事件系统:前后端双向通信
除了命令(前端→后端),Tauri 还支持前后端之间的事件推送。
- 后端向所有窗口广播事件:
use tauri::Manager;
app.emit_all("backend-event", "some data")?;
- 后端向指定窗口发送事件:
window.emit("custom-event", payload)?;
- 前端监听事件:
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('backend-event', (event) => {
console.log(event.payload);
});
- 前端向后端发送事件:用
invoke或使用emit通过全局事件通道(通过App::run()返回的EventLoopProxy等,但更推荐命令模式)。
6. 插件系统
Tauri 2.x 的插件系统允许将功能打包为可复用的 crate。例如,要使用 tauri-plugin-shell 执行系统命令,只需:
- 在
Cargo.toml添加依赖tauri-plugin-shell。 - 在
main.rs中注册:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.run(tauri::generate_context!())
.unwrap();
}
- 前端通过 JavaScript API 调用,如
import { Command } from '@tauri-apps/plugin-shell';。
插件机制让核心保持轻量,按需组合文件系统、剪贴板、通知等功能。
7. 生命周期与退出
Tauri 应用的事件循环会自动运行,直到所有窗口关闭(或调用 app.exit())。你可以通过 Builder 上的 on_window_event 或 on_exit 闭包处理关闭前的清理逻辑:
tauri::Builder::default()
.on_window_event(|event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
// 可以阻止关闭或做保存操作
}
})
.build(tauri::generate_context!())
.expect("error while building tauri application")
.run(|_app_handle, event| {
match event {
tauri::RunEvent::ExitRequested { api, .. } => {
// 处理退出
}
_ => {}
}
});
一句话总结:Rust 主进程是 Tauri 应用的“大脑”,通过 Builder 将所有核心概念(命令、状态、事件、插件)串联起来,让你用最少的代码把系统能力暴露给前端,同时保持安全与高性能。