---
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)._
