在 Tauri 中,前后端交互的核心机制就是命令模式(Command)。这是一种经典的请求-响应模式:前端发起调用,后端执行逻辑并返回结果,整个过程清晰、可预期,并且全程受类型系统约束。
命令是什么?
简单来说,命令就是你在 Rust 端用 #[tauri::command] 宏标记的普通函数。标记后,这个函数会自动暴露给前端 JavaScript,前端可以通过一个统一的 invoke 方法来调用它,就像调用一个异步 API 一样。
// Rust 端:定义一个命令
#[tauri::command]
fn greet(name: String) -> String {
format!("你好,{}!", name)
}
// 前端:调用这个命令
import { invoke } from '@tauri-apps/api/core';
const greeting = await invoke('greet', { name: '小明' });
console.log(greeting); // 输出:你好,小明!
调用关系非常直观:前端传入参数,Rust 处理完返回结果,前端拿到结果,一次交互就结束了。
命令的本质:请求-响应,不建立长连接
与事件监听不同,命令是一次性交互。前端调用一次,后端响应一次,状态不会保持。这种模式特别适合:
- 读取/写入配置
- 查询数据
- 执行单次文件操作
- 触发计算任务(计算完后直接返回结果)
它就像调用一个 HTTP API,只是通信发生在本地的 Rust 进程和 WebView 之间,没有网络延迟,也不需要处理连接断开的问题。
命令的参数与返回类型:类型安全是标配
Tauri 在序列化参数和返回值时使用 JSON(通过 Serde),所以你只需使用普通的 Rust 类型,Tauri 会自动帮你处理转换:
- 前端传
{ name: "小明" }→ Rust 收到String类型的name; - Rust 返回
"Hello"→ 前端拿到 JavaScript 字符串。
如果是更复杂的数据结构,只需确保 Rust 类型实现了 Serialize/Deserialize:
#[derive(serde::Serialize)]
struct User {
id: i32,
username: String,
}
#[tauri::command]
fn get_user(id: i32) -> Result<User, String> {
// ... 获取用户逻辑 ...
Ok(User { id, username: "alice".into() })
}
前端调用后拿到的就是 { id: 1, username: "alice" } 这个 JavaScript 对象,体验完全原生。
错误处理:用 Result 自然表达
命令可以返回 Result<T, E>,Tauri 会自动将 Err 变体转化为前端能够捕获的错误:
#[tauri::command]
fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(&path).map_err(|e| e.to_string())
}
try {
const content = await invoke('read_file', { path: '/some/file.txt' });
console.log(content);
} catch (error) {
console.error('读文件失败:', error); // 错误消息是 Rust 那边传过来的字符串
}
这种模式让错误处理路径和前端异步代码的 try-catch 完美契合,不会丢失错误信息。
命令的注册:让前端“知道”有哪些命令
Rust 端定义了命令后,必须在应用构建时注册它们:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, get_user, read_file])
.run(tauri::generate_context!())
.expect("启动失败");
}
所有注册的命令都会被安全访问控制策略检查:如果某个需要特殊权限的命令没有在配置文件中声明对应的能力,调用时就会被拦截。这确保了即使前端代码中调用了某个命令,没有经过授权的动作也无法执行。
命令模式的实用建议
- 注意参数大小:命令通过 JSON 序列化传递,如果参数是一个巨大的文件数据,考虑使用文件路径而非内容直接传递。
- 避免长时间阻塞:命令默认在主线程执行,如果处理需要较长时间,应使用
async命令或将计算放入tauri::async_runtime::spawn_blocking。 - 命令命名清晰:使用动词开头,比如
read_file、save_config、fetch_user,让前端调用代码一目了然。
命令模式是 Tauri 中最基础、最常用的交互方式,它把前端发起请求、后端执行逻辑这种最自然的需求,用类型安全、低开销的方式实现了。对于大多数工具型应用,90% 的前后端通信都可以通过命令完成。