已发布·持续改进
Tauri 指南 · 3/6
本章目前仅提供英文版。
This chapter explains how a Tauri app works: the relationship between the Rust core and the WebView, commands that let the frontend call Rust, events for two-way notifications, the permission-based security model, and plugins that add features.
A Tauri app consists of a core process written in Rust plus the WebViews that render the UI. Instead of bundling a browser engine, Tauri uses the one provided by the OS: WebView2 (based on Microsoft Edge) on Windows, WKWebView on macOS and iOS, WebKitGTK on Linux and the system WebView on Android. This keeps installers small, but since the rendering engine differs per platform, you should test the UI on every OS you ship to.
Windowing is handled by TAO and WebView integration by WRY, both maintained by the Tauri team. The core can reach windows, menus, the system tray and OS APIs, while code inside the WebView sends requests to the core over IPC (inter-process communication). Checking permissions at that boundary is the heart of Tauri's security.
Annotate a Rust function with #[tauri::command] and register it with generate_handler!, and the frontend can call it by name. Call invoke_handler only once and list every command there; a second call replaces the first.
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
#[tauri::command]
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
return Err("cannot divide by zero".into());
}
Ok(a / b)
}
#[tauri::command]
async fn count_lines(path: String) -> Result<usize, String> {
let text = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
Ok(text.lines().count())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, divide, count_lines])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Arguments and return values travel as JSON, so they must be serializable with serde. On the JavaScript side, pass arguments with camelCase keys; they are matched to the snake_case parameter names in Rust automatically. An Err value becomes a rejected Promise in the frontend. Commands without async run on the main thread, so make long-running work an async command to keep the UI responsive.
import { invoke } from "@tauri-apps/api/core";
const message = await invoke<string>("greet", { name: "Tauri" });
try {
const result = await invoke<number>("divide", { a: 10, b: 0 });
console.log(result);
} catch (error) {
console.error(error); // "cannot divide by zero"
}Commands are request and response; events are fire-and-forget messages that either side can send. Use them when Rust needs to speak first, for progress updates or state changes. Rust emits through the trait, and the frontend subscribes with .
Emitterlistenuse tauri::{AppHandle, Emitter};
#[tauri::command]
fn start_download(app: AppHandle) {
std::thread::spawn(move || {
for progress in [25, 50, 75, 100] {
app.emit("download-progress", progress).unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
}
});
}import { listen } from "@tauri-apps/api/event";
const unlisten = await listen<number>("download-progress", (event) => {
console.log(`Progress: ${event.payload}%`);
});
// Unsubscribe when the view goes away
unlisten();Use emit_to to target a specific window. For streaming large amounts of ordered data, accept a tauri::ipc::Channel as a command argument; channels are faster than events.
Tauri 2 denies WebView access to privileged features by default and lets you allow only what you need in capability files.
fs:allow-read-text-file.{
"identifier": "main-capability",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:allow-open",
{
"identifier": "fs:allow-read-text-file",
"allow": [{ "path": "$APPDATA/**" }]
}
]
}Commands registered by your own app are callable from all windows by default; if you need finer control, you can define permissions for them through an app manifest. On top of that, set a Content Security Policy in app.security.csp to stop untrusted scripts from running.
File system access, dialogs, HTTP, notifications, the shell, key-value storage and more are provided as plugins. Add official plugins with the CLI: it installs the Rust crate and the JavaScript package and registers the plugin in lib.rs. Afterwards, check that the permissions you need are in your capability file.
npm run tauri add dialogimport { open } from "@tauri-apps/plugin-dialog";
const path = await open({
multiple: false,
filters: [{ name: "Text", extensions: ["txt", "md"] }],
});#[tauri::command] plus invoke lets the frontend call Rust; events carry notifications both ways.tauri add.
0 条评论
登录 · 登录后即可发表评论。
来发表第一条评论吧。