mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* tree-wide: update links to https://rustwasm.github.io/wasm-bindgen https://rustwasm.github.io/wasm-bindgen -> https://wasm-bindgen.github.io/wasm-bindgen https://rustwasm.github.io/docs/wasm-pack -> https://drager.github.io/wasm-pack/book/ https://rustwasm.github.io/wasm-pack -> https://github.com/drager/wasm-pack
178 lines
12 KiB
Plaintext
178 lines
12 KiB
Plaintext
---
|
||
title: 'wasm-bindgen'
|
||
sidebar_label: wasm-bindgen
|
||
---
|
||
|
||
[`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) 是一個在 JavaScript 和 Rust 函數之間建立呼叫橋樑的函式庫和工具。它是由 [Rust 和 WebAssembly 工作小組](https://rustwasm.github.io/) 使用 Rust 建構的。
|
||
|
||
Yew 使用 `wasm-bindgen` 透過一些 crate 與瀏覽器進行互動:
|
||
|
||
- [`js-sys`](https://crates.io/crates/js-sys)
|
||
- [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||
- [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||
- [`web-sys`](https://crates.io/crates/web-sys)
|
||
|
||
本節將從更抽象的層次探討這些 crate,以便更容易理解和使用 Yew 中的 `wasm-bindgen` API。要了解有關 `wasm-bindgen` 及其相關 crate 的更深入指南,請查看 [`wasm-bindgen` 指引](https://wasm-bindgen.github.io/wasm-bindgen/)。
|
||
|
||
有關上述 crate 的文檔,請查看 [`wasm-bindgen docs.rs`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/index.html)。
|
||
|
||
:::tip
|
||
使用 `wasm-bindgen` doc.rs 搜尋來尋找已使用 `wasm-bindgen` 匯入的瀏覽器 API 和 JavaScript 類型。
|
||
:::
|
||
|
||
## [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen)
|
||
|
||
這個 crate 為上面的其他 crate 提供了許多構建塊。在本節中,我們只會涵蓋 `wasm-bindgen` crate 的兩個主要領域,即巨集和一些您會一遍又一遍看到的類型/特性。
|
||
|
||
### `#[wasm_bindgen]` macro
|
||
|
||
`#[wasm_bindgen]` 巨集提供了 Rust 和 JavaScript 之間的接口,提供了一個在兩者之間進行轉換的系統。使用這個巨集更為高級,除非您要使用外部 JavaScript 函式庫,否則不應該使用它。 `js-sys` 和 `web-sys` crate 為內建 JavaScript 類型和瀏覽器 API 提供了 `wasm-bindgen` 定義。
|
||
|
||
讓我們透過一個簡單的範例來使用`#[wasm-bindgen]` 巨集來匯入一些特定版本的[`console.log`](https://developer.mozilla.org/en-US/docs/Web/ API/Console/log) 函數。
|
||
|
||
```rust ,no_run
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// 首先讓我們手動綁定 `console.log`,而不使用 `web_sys` 的幫助。
|
||
// 在這裡,我們手動寫 `#[wasm_bindgen]` 註解,我們程式的正確性取決於這些註解的正確性!
|
||
#[wasm_bindgen]
|
||
extern "C" {
|
||
// 在這裡使用 `js_namespace` 來綁定 `console.log(..)` 而不是只有 `log(..)`
|
||
#[wasm_bindgen(js_namespace = console)]
|
||
fn log(s: &str);
|
||
|
||
// `console.log` 是多態的,所以我們可以使用多個簽章綁定它。
|
||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||
fn log_u32(a: u32);
|
||
|
||
// 多個參數也是可以的!
|
||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||
fn log_many(a: &str, b: &str);
|
||
}
|
||
|
||
// 使用導入的函數!
|
||
log("Hello from Rust!");
|
||
log_u32(42);
|
||
log_many("Logging", "many values!");
|
||
```
|
||
|
||
_這個範例是基於 [1.2 使用 console.log 的 `wasm-bindgen` 指引](https://wasm-bindgen.github.io/wasm-bindgen/examples/console-log.html) 改編的。 _
|
||
|
||
### 模擬繼承
|
||
|
||
在 JavaScript 類別之間的繼承是 JavaScript 語言的核心特性,DOM(文件物件模型)是圍繞它設計的。當使用 `wasm-bindgen` 匯入類型時,您也可以新增描述它們繼承關係的屬性。
|
||
|
||
在Rust 中,這種繼承關係使用[`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) 和[`AsRef`](https://doc. rust-lang.org/std/convert/trait.AsRef.html) 特性來表示。這裡舉個例子可能會有所幫助;假設您有三種類型 `A`、`B` 和 `C`,其中 `C` 擴展了 `B`,而 `B` 又擴展了 `A`。
|
||
|
||
在匯入這些類型時,`#[wasm-bindgen]` 巨集將按照下列方式實作 `Deref` 和 `AsRef` 特性:
|
||
|
||
- `C` 可以 `Deref` 到 `B`
|
||
- `B` 可以 `Deref` 到 `A`
|
||
- `C` 可以被 `AsRef` 到 `B`
|
||
- `C` 和 `B` 都可以被 `AsRef` 到 `A`
|
||
|
||
這些實作允許您在 `C` 的實例上呼叫 `A` 的方法,並將 `C` 用作 `&B` 或 `&A`。
|
||
|
||
需要注意的是,使用`#[wasm-bindgen]` 導入的每種類型都有相同的根類型,您可以將其視為上面範例中的`A`,這種類型是[`JsValue`](#jsvalue),下面有它的部分。
|
||
|
||
_[`wasm-bindgen` 指引中的 extends 部分](https://wasm-bindgen.github.io/wasm-bindgen/reference/attributes/on-js-imports/extends.html)_
|
||
|
||
### [`JsValue`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)
|
||
|
||
這是 JavaScript 擁有的物件的表示,這是 `wasm-bindgen` 的根捕獲類型。任何來自`wasm-bindgen` 的型別都是`JsValue`,這是因為JavaScript 沒有強型別系統,因此接受變數`x` 的任何函數都不定義其型別,因此`x` 可以是有效的JavaScript 值;因此`JsValue`。如果您正在使用接受 `JsValue` 的導入函數或類型,那麼任何導入的值在技術上都是有效的。
|
||
|
||
`JsValue` 可以被函數接受,但該函數可能仍然只接受某些類型,這可能會導致panic - 因此在使用原始`wasm-bindgen` API 時,請檢查導入的JavaScript 的文檔,以確定是否會在該值不是某種類型時引發異常(panic)。
|
||
|
||
_[`JsValue` 文件](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)。 _
|
||
|
||
### [`JsCast`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)
|
||
|
||
Rust 有一個強型別系統,而 JavaScript…沒有😞。為了讓 Rust 保持這些強型別但仍然方便,WebAssembly 工作小組提出了一個非常巧妙的特性 `JsCast`。它的工作是幫助您從一個JavaScript "類型" 轉換到另一個"類型",這聽起來很模糊,但它意味著如果您有一個類型,您知道它是另一個類型,那麼您可以使用`JsCast ` 的函數從一個型別跳到另一個型別。當使用 `web-sys`、`wasm_bindgen`、`js-sys` 時,了解這個很好的特性 - 您會注意到許多類型將從這些 crate 中實作 `JsCast`。
|
||
|
||
`JsCast` 提供了轉換的檢查和不檢查方法- 因此在運行時,如果您不確定某個物件是什麼類型,您可以嘗試將其轉換,這將返回可能的失敗類型,如[`Option`] (https://doc.rust-lang.org/std/option/enum.Option.html) 和[`Result`](https://doc.rust-lang.org/std/result/enum.Result. html)。
|
||
|
||
一個常見的例子是在 [`web-sys`](./web-sys.mdx) 中,當您嘗試取得事件的目標時。您可能知道目標元素是什麼,但[`web_sys::Event`](https://wasm-bindgen.github.io/wasm-bindgen/api/web_sys/struct.Event.html) API 總是會回傳一個[` Option<web_sys::EventTarget>`](https://wasm-bindgen.github.io/wasm-bindgen/api/web_sys/struct.Event.html#method.target)。
|
||
您需要將其轉換為元素類型,以便呼叫其方法。
|
||
|
||
```rust
|
||
// 需要先導入這個 Trait
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};
|
||
|
||
fn handle_event(event: Event) {
|
||
let target: EventTarget = event
|
||
.target()
|
||
.expect("I'm sure this event has a target!");
|
||
|
||
// 也許目標是一個選擇元素?
|
||
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
|
||
// 做點別的
|
||
return;
|
||
}
|
||
|
||
// 如果它能確定不是一個選擇元素,那麼我可以肯定它是一個輸入元素!
|
||
let input_element: HtmlInputElement = target.unchecked_into();
|
||
}
|
||
```
|
||
|
||
[`dyn_ref`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_ref) 方法是一個檢查的轉換,回傳一個`Option<&T>`,這表示如果轉換失敗,則可以再次使用原始類型,因此傳回`None`。 [`dyn_into`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) 方法將消耗`self`,這是Rust 中`into` 方法的約定,傳回的類型是`Result<T, Self>`。如果轉換失敗,則原始的 `Self` 值將在 `Err` 中傳回。您可以再試一次或對原始類型進行其他操作。
|
||
|
||
_[`JsCast` documentation](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html)._
|
||
|
||
### [`Closure`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)
|
||
|
||
`Closure` 類型提供了一種將 Rust 閉包傳遞到 JavaScript 的方法,出於健全性原因,傳遞給 JavaScript 的閉包必須具有 `'static` 生命週期。
|
||
|
||
這種類型是一個“句柄”,這意味著每當它被丟棄時,它將使其引用的 JS 閉包無效。在 `Closure` 被丟棄後,對 JS 中閉包的任何使用都會引發異常。
|
||
|
||
當您使用接受型別[`&js_sys::Function`](https://wasm-bindgen.github.io/wasm-bindgen/api/js_sys/struct.Function.html) 的`js-sys` 或`web-sys` API 時,通常會使用`Closure`。在[Events](../html/events.mdx) 頁面的[Using `Closure` 部分](../html/events.mdx#using-closure-verbose) 中可以找到Yew 中使用`Closure` 的範例。
|
||
|
||
_[`Closure` 文件](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html)._
|
||
|
||
## [`js-sys`](https://crates.io/crates/js-sys)
|
||
|
||
`js-sys` crate 提供了 JavaScript 標準內建物件的綁定/導入,包括它們的方法和屬性。
|
||
|
||
這不包括任何 Web API,因為這是 [`web-sys`](./web-sys.mdx) 的作用!
|
||
|
||
_[`js-sys` 文件](https://wasm-bindgen.github.io/wasm-bindgen/api/js_sys/index.html)._
|
||
|
||
## [`wasm-bindgen-futures`](https://crates.io/crates/wasm-bindgen-futures)
|
||
|
||
`wasm-bindgen-futures` crate 提供了一個橋樑,用於將JavaScript Promise 類型作為Rust [`Future`](https://doc.rust-lang.org/stable/std/future/trait.Future.html) 進行處理,並包含將Rust Future 轉換為JavaScript Promise 的實用程式。當在 Rust(wasm)中處理非同步或其他阻塞工作時,這可能很有用,並提供了與 JavaScript 事件和 JavaScript I/O 原語互動的能力。
|
||
|
||
目前這個 crate 中有三個主要介面:
|
||
|
||
1. [`JsFuture`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/struct.JsFuture.html) -
|
||
一個使用[`Promise`](https://wasm-bindgen.github.io/wasm-bindgen/api/js_sys/struct.Promise.html) 建構的類型,然後可以用作`Future<Output=Result<JsValue, JsValue >>`。如果 `Promise` 被解析,這個 `Future` 將解析為 `Ok`,如果 `Promise` 被拒絕,則解析為 `Err`,分別包含 `Promise` 的解析或拒絕值。
|
||
|
||
2. [`future_to_promise`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.future_to_promise.html) -
|
||
將 Rust `Future<Output=Result<JsValue, JsValue>>` 轉換為 JavaScript `Promise`。未來的結果將轉換為 JavaScript 中的已解析或已拒絕 `Promise`。
|
||
|
||
3. [`spawn_local`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html) -
|
||
在目前執行緒上產生一個 `Future<Output = ()>`。這是在 Rust 中運行 Future 的最佳方法,而不是將其發送到 JavaScript。
|
||
|
||
_[`wasm-bindgen-futures` 文件](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/index.html)._
|
||
|
||
### [`spawn_local`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)
|
||
|
||
`spawn_local` 將是 Yew 中 `wasm-bindgen-futures` crate 中最常用的部分,因為這有助於使用具有非同步 API 的函式庫。
|
||
|
||
```rust ,no_run
|
||
use web_sys::console;
|
||
use wasm_bindgen_futures::spawn_local;
|
||
|
||
async fn my_async_fn() -> String { String::from("Hello") }
|
||
|
||
spawn_local(async {
|
||
let mut string = my_async_fn().await;
|
||
string.push_str(", world!");
|
||
// 列印 "Hello, world!"
|
||
console::log_1(&string.into());
|
||
});
|
||
```
|
||
|
||
Yew 還在某些 API 中添加了對 futures 的支持,最值得注意的是您可以創建一個接受 `async` 區塊的 `callback_future` - 這在內部使用了 `spawn_local`。
|
||
|
||
_[`spawn_local` 文件](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen_futures/fn.spawn_local.html)._
|