diff --git a/src/content/docs/zh-cn/plugin/commands.mdx b/src/content/docs/zh-cn/plugin/commands.mdx deleted file mode 100644 index eba0f356e2..0000000000 --- a/src/content/docs/zh-cn/plugin/commands.mdx +++ /dev/null @@ -1,390 +0,0 @@ ---- -title: 从前端调用 Rust ---- - -Tauri 提供了一个简单而强大的 `command` 系统,用于从你的 Web 应用程序中调用 Rust 函数。 -命令可以接受参数和返回值。它们也可以返回错误并且是 `async` 的。 - -## 基本的例子 - -命令在 `src-tauri/src/lib.rs` 文件中定义。要创建命令,只需添加一个函数并用 `#[tauri::command]` 标注它。 - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -fn my_custom_command() { - println!("I was invoked from JavaScript!"); -} -``` - -你必须向构建函数提供命令列表,如下所示。 - -```rust title="src-tauri/src/lib.rs" ins={4} -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![my_custom_command]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -现在,你可以从 JavaScript 代码中调用该命令。 - -```javascript -// 当使用 Tauri API 的 npm 包时 -import { invoke } from '@tauri-apps/api/core'; - -// 使用 Tauri 全局脚本时(如果不使用 npm 包) -// 确保在 `tauri.conf.json` 中将 `app.withGlobalTauri` 设置为 true。 -const invoke = window.__TAURI__.invoke; - -// 使用命令 -invoke('my_custom_command'); -``` - -## 传递参数 - -你的命令处理程序可以接受以下参数。 - -```rust -#[tauri::command] -fn my_custom_command(invoke_message: String) { - println!("I was invoked from JavaScript, with this message: {}", invoke_message); -} -``` - -参数应该作为一个带有 camelCase 键的 JSON 对象传递。 - -```javascript -invoke('my_custom_command', { invokeMessage: 'Hello!' }); -``` - -参数可以是任何类型,只要它们实现了 [`serde::Deserialize`]。 - -请注意,当在 Rust 中使用 snake_case 声明参数时,在 JavaScript 中参数会被转换为 camelCase。 -要在 JavaScript 中使用 snake_case,你必须在 `tauri::command` 语句中声明它。 - -```rust -#[tauri::command(rename_all = "snake_case")] -fn my_custom_command(invoke_message: String) { - println!("I was invoked from JavaScript, with this message: {}", invoke_message); -} -``` - -对应的 JavaScript 代码。 - -```javascript -invoke('my_custom_command', { invoke_message: 'Hello!' }); -``` - -## 返回数据 - -命令处理程序也可以返回数据。 - -```rust -#[tauri::command] -fn my_custom_command() -> String { - "Hello from Rust!".into() -} -``` - -`invoke` 函数返回一个 promise,其 resolve 接收一个返回值。 - -```javascript -invoke('my_custom_command').then((message) => console.log(message)); -``` - -返回的数据可以是任何类型,只要它实现了 [`serde::Serialize`]。 - -## 错误处理 - -如果你的处理程序可能失败并且需要能够返回一个错误,让函数返回一个 `Result`。 - -```rust -#[tauri::command] -fn my_custom_command() -> Result { - // If something fails - Err("This failed!".into()) - // If it worked - Ok("This worked!".into()) -} -``` - -如果命令返回错误,promise 将 reject,否则 resolve。 - -```javascript -invoke('my_custom_command') - .then((message) => console.log(message)) - .catch((error) => console.error(error)); -``` - -如上所述,从命令返回的所有内容都必须实现 [`serde::Serialize`],包括错误。 -如果您正在处理来自 Rust 的 std 库或外部 crate 的错误类型,则可能会出现问题,因为大多数错误类型都没有实现它。 -一般情况下,你可以使用 `map_err` 将这些错误转换为 `String`。 - -```rust -#[tauri::command] -fn my_custom_command() -> Result<(), String> { - // 这将返回一个错误 - std::fs::File::open("path/that/does/not/exist").map_err(|err| err.to_string())?; - // 成功返回空 - Ok(()) -} -``` - -因为这不是很符合习惯,你可能想要创建自己的错误类型来实现 `serde::Serialize`。在下面的例子中,我们使用 [`thiserror`] 创建错误类型。 -它允许你通过派生 `thiserror::Error` 特征将枚举转换为错误类型。你可以查阅它的文档了解更多细节。 - -```rust -// 创建 error 类型,表示程序中可能出现的所有错误 -#[derive(Debug, thiserror::Error)] -enum Error { - #[error(transparent)] - Io(#[from] std::io::Error) -} - -// 我们必须手动实现 serde::Serialize -impl serde::Serialize for Error { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - serializer.serialize_str(self.to_string().as_ref()) - } -} - -#[tauri::command] -fn my_custom_command() -> Result<(), Error> { - // 这将返回一个错误 - std::fs::File::open("path/that/does/not/exist")?; - // 成功返回空 - Ok(()) -} -``` - -自定义错误类型的优点是明列所有可能的错误,以便读者快速识别可能发生的错误。这为别人(和你自己)在以后审查和重构代码时节省了大量的时间。
-它还让你可以完全控制错误类型被序列化的方式。在上面的例子中,我们简单地将错误消息作为字符串返回,但是你可以为每个错误分配一个类似于 C 的代码,这样你可以更容易地将它映射到一个看起来类似的 TypeScript 错误枚举。 - -## 异步命令 - -异步函数在 Tauri 中有利于执行繁重的工作,不会导致 UI 冻结或减慢。 - -:::note - -异步命令使用 [`async_runtime::spawn`] 在单独的线程上执行。 -没有 _async_ 关键字的命令将在主线程上执行,除非定义了 _#[tauri::command(async)]_。 - -::: - -**如果你的命令需要异步运行,只需将其声明为 `async`。** - -:::caution - -使用 Tauri 创建异步函数时需要小心目前,你不能简单地在异步函数的签名中包含借用的参数。一些常见的类型示例是 `&str` 和 `State<'_, Data>`。 -这里追踪了这个限制: https://github.com/tauri-apps/tauri/issues/2533 和解决方法如下所示。 - -::: - -在使用借用的类型时,必须进行额外的更改。这是你的两个主要选择。 - -**选项 1**:将类型(如 `&str`)转换为不允许借用的类似类型(如 `String`)。 这可能不适用于所有类型,例如 `State<'_, Data>`。 - -_示例:_ - -```rust -// 使用 String 而不是 &str 声明异步函数,因为 &str 是借用的,因此不受支持 -#[tauri::command] -async fn my_custom_command(value: String) -> String { - // 调用另一个异步函数并等待它完成 - some_async_function().await; - value -} -``` - -**选项 2**:将返回类型包装在一个 [`Result`] 中。这个实现起来有点难,但应该适用于所有类型。 - -使用返回类型 `Result`,将 `a` 替换为你想要返回的类型,或者如果你不想返回任何类型,则使用 `()` ,如果发生错误,则将 `b` 替换为错误类型,或者如果你不想返回任何可选错误,则使用 `()`。例如: - -- `Result` 返回一个字符串,并且没有错误。 -- `Result<(), ()>` 什么都不返回。 -- `Result` 返回一个布尔值或一个错误,如上面的[错误处理](#错误处理)部分所示。 - -_示例:_ - -```rust -// 返回一个 Result 来绕过借用问题 -#[tauri::command] -async fn my_custom_command(value: &str) -> Result { - // 调用另一个异步函数并等待它完成 - some_async_function().await; - // 注意,现在必须将返回值包装在 `Ok()` 中。 - Ok(format!(value)) -} -``` - -#### 从 JavaScript 调用 - -因为在 JavaScript 中调用这个命令会返回一个 promise,所以它的工作方式和其他命令一样。 - -```javascript -invoke('my_custom_command', { value: 'Hello, Async!' }).then(() => - console.log('Completed!') -); -``` - -## 在命令中访问 WebviewWindow - -命令可以访问调用消息的 `WebviewWindow` 实例。 - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -async fn my_custom_command(webview_window: tauri::WebviewWindow) { - println!("WebviewWindow: {}", webview_window.label()); -} -``` - -## 在命令中访问 Window - -命令可以访问调用消息的 `Window` 实例。 - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -async fn my_custom_command(window: tauri::Window) { - println!("Window: {}", window.label()); -} -``` - -你也可以从 `WebviewWindow` 中访问 `Window` 实例: - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -async fn my_custom_command(webview_window: tauri::WebviewWindow) { - let window: tauri::Window = webview_window.as_ref().window(); - println!("Window: {}", window.label()); -} -``` - -## 在命令中访问 AppHandle - -命令可以访问 `AppHandle` 实例。 - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -async fn my_custom_command(app_handle: tauri::AppHandle) { - let app_dir = app_handle.path_resolver().app_dir(); - use tauri::GlobalShortcutManager; - app_handle.global_shortcut_manager().register("CTRL + U", move || {}); -} -``` - -## 访问托管状态 - -Tauri 可以使用 `Tauri::Builder` 上的 `manage` 函数来管理状态。 -可以使用 `tauri::State` 命令访问状态。 - -```rust title="src-tauri/src/lib.rs" -struct MyState(String); - -#[tauri::command] -fn my_custom_command(state: tauri::State) { - assert_eq!(state.0 == "some state value", true); -} - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .manage(MyState("some state value".into())) - .invoke_handler(tauri::generate_handler![my_custom_command]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -## 创建多个命令 - -`tauri::generate_handler!` 宏接受一个命令数组作为参数, -为了注册多个命令,你不能多次调用 invoke_handler, -只有最后一次调用才会被使用,你必须将每个命令传递给 `tauri::generate_handler!` 的单次调用。 - -```rust title="src-tauri/src/lib.rs" -#[tauri::command] -fn cmd_a() -> String { - "Command a" -} -#[tauri::command] -fn cmd_b() -> String { - "Command b" -} - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![cmd_a, cmd_b]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -## 完整的示例 - -上面的任何一个或所有功能都可以组合起来。 - -```rust title="src-tauri/src/lib.rs" -struct Database; - -#[derive(serde::Serialize)] -struct CustomResponse { - message: String, - other_val: usize, -} - -async fn some_other_function() -> Option { - Some("response".into()) -} - -#[tauri::command] -async fn my_custom_command( - window: tauri::Window, - number: usize, - database: tauri::State<'_, Database>, -) -> Result { - println!("Called from {}", window.label()); - let result: Option = some_other_function().await; - if let Some(message) = result { - Ok(CustomResponse { - message, - other_val: 42 + number, - }) - } else { - Err("No result".into()) - } -} - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .manage(Database {}) - .invoke_handler(tauri::generate_handler![my_custom_command]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -```javascript -import { invoke } from '@tauri-apps/api/core'; - -// 从 JavaScript 调用 -invoke('my_custom_command', { - number: 42, -}) - .then((res) => - console.log(`Message: ${res.message}, Other Val: ${res.other_val}`) - ) - .catch((e) => console.error(e)); -``` - -[`async_runtime::spawn`]: https://docs.rs/tauri/2.0.0/tauri/async_runtime/fn.spawn.html -[`serde::serialize`]: https://docs.serde.rs/serde/trait.Serialize.html -[`serde::deserialize`]: https://docs.serde.rs/serde/trait.Deserialize.html -[`thiserror`]: https://github.com/dtolnay/thiserror -[`result`]: https://doc.rust-lang.org/std/result/index.html