wasm-bindgen
wasm-bindgen
是一個在 JavaScript 和 Rust 函數之間建立呼叫橋樑的函式庫和工具。它是由 Rust 和 WebAssembly 工作小組 使用 Rust 建構的。
Yew 使用 wasm-bindgen
透過一些 crate 與瀏覽器進行互動:
本節將從更抽象的層次探討這些 crate,以便更容易理解和使用 Yew 中的 wasm-bindgen
API。要了解有關 wasm-bindgen
及其相關 crate 的更深入指南,請查看 wasm-bindgen
指引。
有關上述 crate 的文檔,請查看 wasm-bindgen docs.rs
。
使用 wasm-bindgen
doc.rs 搜尋來尋找已使用 wasm-bindgen
匯入的瀏覽器 API 和 JavaScript 類型。
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) 函數。
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
指引 改編的。 _
模擬繼承
在 JavaScript 類別之間的繼承是 JavaScript 語言的核心特性,DOM(文件物件模型)是圍繞它設計的。當使用 wasm-bindgen
匯入類型時,您也可以新增描述它們繼承關係的屬性。
在Rust 中,這種繼承關係使用Deref
和[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
這是 JavaScript 擁有的物件的表示,這是 wasm-bindgen
的根捕獲類型。任何來自wasm-bindgen
的型別都是JsValue
,這是因為JavaScript 沒有強型別系統,因此接受變數x
的任何函數都不定義其型別,因此x
可以是有效的JavaScript 值;因此JsValue
。如果您正在使用接受 JsValue
的導入函數或類型,那麼任何導入的值在技術上都是有效的。
JsValue
可以被函數接受,但該函數可能仍然只接受某些類型,這可能會導致panic - 因此在使用原始wasm-bindgen
API 時,請檢查導入的JavaScript 的文檔,以確定是否會在該值不是某種類型時引發異常(panic)。
_JsValue
文件。 _
JsCast
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::Event
API 總是會回傳一個 Option<web_sys::EventTarget>
。
您需要將其轉換為元素類型,以便呼叫其方法。
// 需要先導入這個 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
方法是一個檢查的轉換,回傳一個Option<&T>
,這表示如果轉換失敗,則可以再次使用原始類型,因此傳回None
。 dyn_into
方法將消耗self
,這是Rust 中into
方法的約定,傳回的類型是Result<T, Self>
。如果轉換失敗,則原始的 Self
值將在 Err
中傳回。您可以再試一次或對原始類型進行其他操作。
Closure
Closure
類型提供了一種將 Rust 閉包傳遞到 JavaScript 的方法,出於健全性原因,傳遞給 JavaScript 的閉包必須具有 'static
生命週期。
這種類型是一個“句柄”,這意味著每當它被丟棄時,它將使其引用的 JS 閉包無效。在 Closure
被丟棄後,對 JS 中閉包的任何使用都會引發異常。
當您使用接受型別&js_sys::Function
的js-sys
或web-sys
API 時,通常會使用Closure
。在Events 頁面的Using Closure
部分 中可以找到Yew 中使用Closure
的範例。
js-sys
js-sys
crate 提供了 JavaScript 標準內建物件的綁定/導入,包括它們的方法和屬性。
這不包括任何 Web API,因為這是 web-sys
的作用!
wasm-bindgen-futures
wasm-bindgen-futures
crate 提供了一個橋樑,用於將JavaScript Promise 類型作為Rust Future
進行處理,並包含將Rust Future 轉換為JavaScript Promise 的實用程式。當在 Rust(wasm)中處理非同步或其他阻塞工作時,這可能很有用,並提供了與 JavaScript 事件和 JavaScript I/O 原語互動的能力。
目前這個 crate 中有三個主要介面:
-
JsFuture
- 一個使用Promise
建構的類型,然後可以用作Future<Output=Result<JsValue, JsValue >>
。如果Promise
被解析,這個Future
將解析為Ok
,如果Promise
被拒絕,則解析為Err
,分別包含Promise
的解析或拒絕值。 -
future_to_promise
- 將 RustFuture<Output=Result<JsValue, JsValue>>
轉換為 JavaScriptPromise
。未來的結果將轉換為 JavaScript 中的已解析或已拒絕Promise
。 -
spawn_local
- 在目前執行緒上產生一個Future<Output = ()>
。這是在 Rust 中運行 Future 的最佳方法,而不是將其發送到 JavaScript。
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!");
// 列印 "Hello, world!"
console::log_1(&string.into());
});
Yew 還在某些 API 中添加了對 futures 的支持,最值得注意的是您可以創建一個接受 async
區塊的 callback_future
- 這在內部使用了 spawn_local
。