Tauri 的插件系统能让你用 Rust 封装系统级能力,然后在前端通过 JS 调用。这篇介绍怎么从零写一个 Tauri 插件,实现系统通知、托盘、窗口管理等功能。
Tauri 插件架构
Tauri 插件本质上是一个实现了 tauri::plugin::Plugin trait 的 Rust struct。一个插件可以:
- 注册 Tauri commands(供前端 JS 调用)
- 监听应用生命周期事件(setup、on_event 等)
- 管理自己的状态
插件的基本骨架如下:
use tauri::{
plugin::{Builder, TauriPlugin},
Runtime, Manager,
};
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("my-plugin")
.invoke_handler(tauri::generate_handler![
notify_send,
window_set_title,
])
.setup(|app| {
// 插件初始化逻辑
println!("my-plugin initialized");
Ok(())
})
.build()
}
在主应用中注册:
fn main() {
tauri::Builder::default()
.plugin(my_plugin::init())
.run(tauri::generate_context!())
.expect("error running app");
}
调用系统通知 API
用 notify-rust crate 可以在桌面发系统通知。先在插件的 Cargo.toml 加依赖:
[dependencies]
notify-rust = "4"
然后写 command:
use notify_rust::Notification;
#[tauri::command]
fn notify_send(title: String, body: String) -> Result<(), String> {
Notification::new()
.summary(&title)
.body(&body)
.icon("dialog-information")
.timeout(5000)
.show()
.map_err(|e| e.to_string())?;
Ok(())
}
前端调用:
import { invoke } from '@tauri-apps/api/tauri';
async function sendNotification(title: string, body: string) {
await invoke('notify_send', { title, body });
}
系统托盘
Tauri 内建了系统托盘支持,但通过插件可以做更灵活的控制:
use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu, SystemTrayEvent};
pub fn create_tray() -> SystemTray {
let show = CustomMenuItem::new("show".to_string(), "显示窗口");
let hide = CustomMenuItem::new("hide".to_string(), "隐藏窗口");
let quit = CustomMenuItem::new("quit".to_string(), "退出");
let menu = SystemTrayMenu::new()
.add_item(show)
.add_item(hide)
.add_native_item(tauri::SystemTrayMenuItem::Separator)
.add_item(quit);
SystemTray::new().with_menu(menu)
}
pub fn handle_tray_event<R: Runtime>(app: &tauri::AppHandle<R>, event: SystemTrayEvent) {
match event {
SystemTrayEvent::MenuItemClick { id, .. } => {
match id.as_str() {
"show" => {
let window = app.get_window("main").unwrap();
window.show().unwrap();
window.set_focus().unwrap();
}
"hide" => {
let window = app.get_window("main").unwrap();
window.hide().unwrap();
}
"quit" => std::process::exit(0),
_ => {}
}
}
SystemTrayEvent::DoubleClick { .. } => {
let window = app.get_window("main").unwrap();
window.show().unwrap();
window.set_focus().unwrap();
}
_ => {}
}
}
窗口管理
Tauri 的 WindowBuilder 可以在运行时动态创建窗口。封装成插件 command 后,前端就能按需开新窗口:
#[tauri::command]
async fn window_create<R: Runtime>(
app: tauri::AppHandle<R>,
label: String,
title: String,
url: String,
width: f64,
height: f64,
) -> Result<(), String> {
tauri::WindowBuilder::new(&app, &label, tauri::WindowUrl::App(url.into()))
.title(&title)
.inner_size(width, height)
.center()
.build()
.map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
async fn window_set_title<R: Runtime>(
app: tauri::AppHandle<R>,
label: String,
title: String,
) -> Result<(), String> {
let win = app.get_window(&label).ok_or("Window not found")?;
win.set_title(&title).map_err(|e| e.to_string())?;
Ok(())
}
把这些能力包装成插件之后,前端可以非常方便地调用系统级功能,而不需要关心底层平台差异。Tauri 的插件机制设计得很干净,Rust 端处理系统交互、前端只管调用,职责分离很清晰。