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
213 lines
7.8 KiB
Plaintext
213 lines
7.8 KiB
Plaintext
---
|
||
title: 'web-sys'
|
||
description: 'web-sys crate 为 Web API 提供绑定。'
|
||
---
|
||
|
||
import Tabs from '@theme/Tabs'
|
||
import TabItem from '@theme/TabItem'
|
||
|
||
[`web-sys` crate](https://crates.io/crates/web-sys) 为 Web API 提供绑定。这是从浏览器 WebIDL 生成的,这就是为什么有些名称如此之长,有些类型如此模糊的原因。
|
||
|
||
## `web-sys` 中的特性 (features)
|
||
|
||
`web-sys` crate 中启用了所有特性可能会给 Wasm 应用程序增加很多冗余。为了解决这个问题,大多数类型都是通过启用 features 进行控制的,这样你只需要包含你的应用程序所需的类型。Yew 启用了 `web-sys` 的几个特性,并在其公共 API 中公开了一些类型。你通常需要自行将 `web-sys` 添加为依赖项。
|
||
|
||
## `web-sys` 中的继承
|
||
|
||
在[模拟继承](./wasm-bindgen.mdx#simulating-inheritance)部分,你可以了解到 Rust 通常提供了一种模拟 JavaScript 中继承的方法。这在 `web-sys` 中非常重要,因为了解一个类型上有哪些方法意味着了解它的继承。
|
||
|
||
这一部分将查看一个特定的元素,并使用 Rust 调用 [`Deref::deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html#tymethod.deref) 列出其继承,直到该值为 [`JsValue`](./wasm-bindgen.mdx#jsvalue)。
|
||
|
||
```rust
|
||
use std::ops::Deref;
|
||
use web_sys::{
|
||
Element,
|
||
EventTarget,
|
||
HtmlElement,
|
||
HtmlTextAreaElement,
|
||
Node,
|
||
};
|
||
|
||
fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
|
||
// HtmlTextAreaElement 是 HTML 中的 <textarea>。
|
||
let html_element: &HtmlElement = text_area.deref();
|
||
|
||
let element: &Element = html_element.deref();
|
||
|
||
let node: &Node = element.deref();
|
||
|
||
let event_target: &EventTarget = node.deref();
|
||
|
||
// 注意我们现在已经从 web-sys 类型转移到内置的 JavaScript 类型,
|
||
// 这些类型在 js-sys crate 中。
|
||
let object: &js_sys::Object = event_target.deref();
|
||
|
||
// 注意我们现在已经从 js-sys 类型转移到 wasm-bindgen crate 中的根 JsValue。
|
||
let js_value: &wasm_bindgen::JsValue = object.deref();
|
||
|
||
// 这样使用 deref 意味着我们必须手动遍历继承树。
|
||
// 但是,您可以在 HtmlTextAreaElement 类型上调用 JsValue 方法。
|
||
assert!(!text_area.is_string());
|
||
|
||
// 这个空函数只是为了证明我们可以将 HtmlTextAreaElement 作为 &EventTarget 传递。
|
||
fn this_function_only_takes_event_targets(targets: &EventTarget) {};
|
||
|
||
// 编译器将沿着 deref 链向下走,以匹配这里的类型。
|
||
this_function_only_takes_event_targets(&text_area);
|
||
|
||
// AsRef 实现允许您将 HtmlTextAreaElement 视为 &EventTarget。
|
||
let event_target: &EventTarget = text_area.as_ref();
|
||
|
||
}
|
||
```
|
||
|
||
_[`wasm-bindgen` 指引中的 `web-sys` 继承](https://wasm-bindgen.github.io/wasm-bindgen/web-sys/inheritance.html)_
|
||
|
||
## `NodeRef` 中的 `Node`
|
||
|
||
Yew 使用 [`NodeRef`](concepts/function-components/node-refs.mdx) 来提供一种方式来保留由 [`html!`](concepts/html/introduction.mdx) 宏创建的 `Node` 的引用。`NodeRef` 中的 `Node` 指的是 [`web_sys::Node`](https://wasm-bindgen.github.io/wasm-bindgen/api/web_sys/struct.Node.html)。`NodeRef::get` 方法将返回一个 `Option<Node>` 值,但是,在 Yew 中,大多数情况下,您希望将此值转换为特定元素,以便使用其特定方法。如果存在,可以使用 [`JsCast`](./wasm-bindgen.mdx#JsCast) 对 `Node` 值进行转换,但是 Yew 提供了 `NodeRef::cast` 方法来执行此转换,以方便使用,因此您不一定需要为 `JsCast` 特性包含 `wasm-bindgen` 依赖项。
|
||
|
||
下面的两个代码块本质上是相同的,第一个使用 `NodeRef::cast`,第二个使用 [`JsCast::dyn_into`](https://wasm-bindgen.github.io/wasm-bindgen/api/wasm_bindgen/trait.JsCast.html#method.dyn_into) 在 `NodeRef::get` 返回的 `web_sys::Node` 上。
|
||
|
||
<Tabs>
|
||
<TabItem value="Using NodeRef::cast" label="Using NodeRef::cast">
|
||
|
||
```rust
|
||
use web_sys::HtmlInputElement;
|
||
use yew::NodeRef;
|
||
|
||
fn with_node_ref_cast(node_ref: NodeRef) {
|
||
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
|
||
// 在这里处理 HtmlInputElement
|
||
}
|
||
}
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="Using NodeRef::get" label="Using NodeRef::get">
|
||
|
||
```rust
|
||
use wasm_bindgen::JsCast;
|
||
use web_sys::HtmlInputElement;
|
||
use yew::NodeRef;
|
||
|
||
fn with_jscast(node_ref: NodeRef) {
|
||
if let Some(input) = node_ref
|
||
.get()
|
||
.and_then(|node| node.dyn_into::<HtmlInputElement>().ok()) {
|
||
// 在这里处理 HtmlInputElement
|
||
}
|
||
}
|
||
```
|
||
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
## JavaScript 重构为 Rust 的示例
|
||
|
||
这一节展示了如何将与 Web API 交互的 JavaScript 代码示例重写为 Rust 中的 `web-sys`。
|
||
|
||
### JavaScript 示例
|
||
|
||
```js
|
||
document.getElementById('mousemoveme').onmousemove = (e) => {
|
||
// e 为鼠标事件对象
|
||
var rect = e.target.getBoundingClientRect()
|
||
var x = e.clientX - rect.left // 元素内的 x 位置。
|
||
var y = e.clientY - rect.top // 元素内的 y 位置。
|
||
console.log('Left? : ' + x + ' ; Top? : ' + y + '.')
|
||
}
|
||
```
|
||
|
||
### 用 `web-sys` 重写的示例
|
||
|
||
仅使用 `web-sys`,上面的 JavaScript 示例可以这样实现:
|
||
|
||
```toml title=Cargo.toml
|
||
[dependencies]
|
||
wasm-bindgen = "0.2"
|
||
|
||
[dependencies.web-sys]
|
||
version = "0.3"
|
||
# 需要启用所有我们想要使用的 web-sys 特性!
|
||
features = [
|
||
"console",
|
||
"Document",
|
||
"HtmlElement",
|
||
"MouseEvent",
|
||
"DomRect",
|
||
]
|
||
```
|
||
|
||
```rust ,no_run
|
||
use wasm_bindgen::{prelude::Closure, JsCast};
|
||
use web_sys::{console, Document, HtmlElement, MouseEvent};
|
||
|
||
let mousemove = Closure::<dyn Fn(MouseEvent)>::wrap(Box::new(|e| {
|
||
let rect = e
|
||
.target()
|
||
.expect("mouse event doesn't have a target")
|
||
.dyn_into::<HtmlElement>()
|
||
.expect("event target should be of type HtmlElement")
|
||
.get_bounding_client_rect();
|
||
let x = (e.client_x() as f64) - rect.left();
|
||
let y = (e.client_y() as f64) - rect.top();
|
||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||
}));
|
||
|
||
Document::new()
|
||
.expect("global document not set")
|
||
.get_element_by_id("mousemoveme")
|
||
.expect("element with id `mousemoveme` not present")
|
||
.unchecked_into::<HtmlElement>()
|
||
.set_onmousemove(mousemove.as_ref().dyn_ref());
|
||
|
||
// 我们现在需要保存 `mousemove` 闭包,以便在事件触发时闭包仍然在内存中。
|
||
```
|
||
|
||
这个版本更加冗长,但你可能会注意到其中的一部分是由于失败类型提醒我们,一些函数调用有必须保持的不变量,否则将在 Rust 中引发 panic。另一个冗长的部分是调用 `JsCast` 来将不同类型转换为特定类型,以便调用其特定方法。
|
||
|
||
### 用 Yew 重写的示例
|
||
|
||
在 Yew 中,您将主要创建 [`Callback`](concepts/function-components/callbacks.mdx) 以在 [`html!`](concepts/html/introduction.mdx) 宏中使用,因此示例将使用这种方法,而不是完全复制上面的方法:
|
||
|
||
```toml title=Cargo.toml
|
||
[dependencies.web-sys]
|
||
version = "0.3"
|
||
# 我们需要启用 `DomRect` 特性以使用 `get_bounding_client_rect` 方法。
|
||
features = [
|
||
"console",
|
||
"HtmlElement",
|
||
"MouseEvent",
|
||
"DomRect",
|
||
]
|
||
|
||
```
|
||
|
||
```rust
|
||
use web_sys::{console, HtmlElement, MouseEvent};
|
||
use yew::{
|
||
html,
|
||
Callback, TargetCast,
|
||
};
|
||
|
||
let onmousemove = Callback::from(|e: MouseEvent| {
|
||
if let Some(target) = e.target_dyn_into::<HtmlElement>() {
|
||
let rect = target.get_bounding_client_rect();
|
||
let x = (e.client_x() as f64) - rect.left();
|
||
let y = (e.client_y() as f64) - rect.top();
|
||
console::log_1(&format!("Left? : {} ; Top? : {}", x, y).into());
|
||
}
|
||
});
|
||
|
||
html! {
|
||
<div id="mousemoveme" {onmousemove}></div>
|
||
};
|
||
```
|
||
|
||
## 补充依赖库
|
||
|
||
`web-sys` 是 Web API 的原始绑定,因此在 Rust 中会有一些痛苦,因为它并不是为 Rust 或甚至强类型系统设计的,这就是社区 crate 提供了对 `web-sys` 的抽象,以提供更符合 Rust 习惯的 API。
|
||
|
||
_[补充依赖库清单](/community/external-libs)_
|