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