跳到主要內容
版本:0.21

wasm-bindgen

wasm-bindgen 是個函式庫和工具,能促進 Wasm 模組和 JavaScript 之間的高階互動;它由 Rust 和 WebAssembly 工作小組 使用 Rust 編寫。

Yew 使用 wasm-bindgen 透過多個 crate 來與瀏覽器互動。

本節將概略介紹其中一些 crate,藉此讓理解和使用 wasm-bindgen API 與 Yew 更為容易。如果您需要有關 wasm-bindgen 及其相關 crate 的更深入指南,請查看 wasm-bindgen 使用指南

如需上述 crate 的說明文件,請查看 wasm-bindgen docs.rs

提示

使用 wasm-bindgen doc.rs 搜尋,以找到使用 wasm-bindgen 匯入的瀏覽器 API 和 JavaScript 類型。

wasm-bindgen

此程式箱為上面所有程式箱的其餘部分提供了許多構件。在此部分中,我們僅介紹 wasm-bindgen 程式箱的兩個主要部分,即巨集和您將看到不斷重複出現的一些類型/特質。

#[wasm_bindgen] 巨集

#[wasm_bindgen] 巨集提供 Rust 和 JavaScript 之間的介面,提供在兩者之間進行轉換的系統。此巨集的使用較為進階,除非您嘗試使用外部 JavaScript 函式庫,否則您不需要用到它。js-sysweb-sys 程式箱公開了內建 JavaScript 類型和瀏覽器 API 的 wasm-bindgen 定義。

我們來看一個簡單的範例,展示如何使用 #[wasm-bindgen] 巨集來匯入 console.log 函式的特定風味。

use wasm_bindgen::prelude::*;

// First up let's take a look of binding `console.log` manually, without the
// help of `web_sys`. Here we're writing the `#[wasm_bindgen]` annotations
// manually ourselves, and the correctness of our program relies on the
// correctness of these annotations!
#[wasm_bindgen]
extern "C" {

// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);

// The `console.log` is quite polymorphic, so we can bind it with multiple
// signatures. Note that we need to use `js_name` to ensure we always call
// `log` in JS.
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);

// Multiple arguments too!
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}

// using the imported functions!
log("Hello from Rust!");
log_u32(42);
log_many("Logging", "many values!");

此範例改編自 wasm-bindgen 指南的 1.2 使用 console.log.

模擬繼承

JavaScript 類別之間的繼承是 JavaScript 語言的核心功能,Document Object Model (DOM) 是圍繞著它設計的。當使用 wasm-bindgen 匯入類型時,您也可以加入描述其繼承的屬性。

在 Rust 中,此繼承是使用 DerefAsRef 特質表示的。舉例來說:假設您有三個類型 ABC,其中 C 擴充 B,而 B 則擴充 A

在匯入這些類型時,#[wasm-bindgen] 巨集將以下列方式實作 DerefAsRef 特質

  • C 可以 DerefB
  • B 可以 DerefA
  • C 可以對 B 進行 AsRef
  • CB 都可以對 A 進行 AsRef

這些實作允許您對 C 的實例呼叫 A 的方法,並像使用 &B&A 一樣使用 C

請注意,使用 #[wasm-bindgen] 匯入的每個單一類型都有相同的根類型,你可以將其視為上方範例中的 A,此類型是 JsValue,下方有其區段。

延伸 wasm-bindgen 指南的區段

JsValue

這是 JavaScript 所擁有的物件的表示法,這是 wasm-bindgen 的根部通抓類型。從 wasm-bindgen 而來的任何類型都是 JsValue,這是因為 JavaScript 沒有強型別系統,所以接受變數 x 的任何函式都不會定義其類型,因此 x 可以是有效的 JavaScript 值;因此 JsValue。如果你使用接受 JsValue 的匯入函式或類型,那麼任何匯入值在技術上都是有效的。

JsValue 可以被函式接受,但該函式可能仍然只接受特定類型,這可能導致恐慌 - 所以在使用原始 wasm-bindgen API 時,請查看要匯入的 JavaScript 的文件,以了解如果該值不是特定類型是否會引發例外(恐慌)。

JsValue 文件.

JsCast

Rust 有強型別系統,JavaScript 則沒有 😞。為了讓 Rust 維持這些強型別但仍然方便,WebAssembly 小組想出了一個很不錯的 Trait JsCast。它的工作是幫助您從一個 JavaScript「類型」移動到另一個類型,這聽起來很含糊,但它的意思是如果您有一個已知的另一種類型,那麼您可以使用 JsCast 的函式從一種類型跳到另一種類型。在使用 web-syswasm_bindgenjs-sys 時,這是個不錯的 Trait - 您會注意到很多類型會從那些板條箱實作 JsCast

JsCast 提供了檢查過的和未檢查過的轉型方法 - 因此,如果您在執行時期不確定某個物件的類型,您可以嘗試將其轉型,它回傳可能的失敗類型,例如 OptionResult

web-sys 中的一個常見範例是當嘗試取得事件目標時。您可能知道目標元素為何,但 web_sys::Event API 永遠會傳回 Option<web_sys::EventTarget>。您需要將其轉型為元素類型以呼叫其方法。

// need to import the 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!");

// maybe the target is a select element?
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
// do something amazing here
return;
}

// if it wasn't a select element then I KNOW it's a input element!
let input_element: HtmlInputElement = target.unchecked_into();
}

dyn_ref 方法是一種檢查轉型,會傳回一個 Option<&T>,這表示如果轉型失敗,原始類型仍可再次使用,因此傳回 None。dyn_into 方法會使用掉 self,這是 Rust 中 into 方法的慣例,且傳回的類型是 Result<T, Self>。如果轉型失敗,原始 Self 值會在 Err 中傳回。您可以重試或使用原始類型執行其他操作。

JsCast 文件.

Closure

Closure 類型提供一種方式可將 Rust 函數式封包傳輸至 JavaScript,傳遞至 JavaScript 的封包必須有 'static 生命周期,以確保健全性。

此類型是一種「處理常式」,換句話說,當放棄它時,它可能會使它所引用的 JS 封包失效。在 Closure 已放棄後,於 JS 中使用封包會引發例外狀況。

當使用接受 js_sys::Function 類型的 js-sys 或 web-sys API 時,會經常使用 Closure。可以在 事件 頁面的 使用 Closure 區段找到在 Yew 中使用 Closure 的範例。

Closure 文件.

js-sys

js-sys crate 提供了 JavaScript 標準內建物件的繫結/輸入,包括它們的方法和屬性。

這不包括任何網頁 API,這是 web-sys 的功用!

js-sys 文件.

wasm-bindgen-futures

wasm-bindgen-futures crate 提供了一個將 JavaScript Promise 型別當作 Rust Future 使用的橋樑,並包含將 Rust Future 轉換為 JavaScript Promise 的工具程式。這在 Rust (wasm) 處理非同步或其他作業時很有用,並提供與 JavaScript 事件和 JavaScript I/O 原生元件進行串接的能力。

這個 crate 目前有三個主要的介面

  1. JsFuture - 一個由 Promise 建構的型別,可以作為 Future<Output=Result<JsValue, JsValue>> 使用。這個 Future 會解析為 Ok,如果 Promise 是已處理的,並解析為 Err 如果 Promise 是被拒絕的,分別包含已處理或被拒絕的值來自 Promise

  2. future_to_promise - 將 Rust Future<Output=Result<JsValue, JsValue>> 轉換成 JavaScript Promise。這個將來的結果會轉譯到一個 JavaScript 已處理或被拒絕的 Promise

  3. spawn_local - 在目前的執行緒產生 Future<Output = ()>。這是 Rust 中執行 Future 的最佳方式,不必把它送到 JavaScript。

wasm-bindgen-futures 文件.

spawn_local

spawn_local 將會變成 Yew 中最常使用的 wasm-bindgen-futures crate 的一部分,因為它在使用具有非同步 API 的程式庫時很有用。

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!");
// console log "Hello, world!"
console::log_1(&string.into());
});

Yew 也在特定的 API 中添加了對 Futures 的支援,最值得注意的是你可以建立一個接受非同步區塊的 callback_future - 它會在內部使用 spawn_local

spawn_local 文件.