Skip to main content
Version: Next

web-sys

web-sys crate 为 Web API 提供绑定。这是从浏览器 WebIDL 生成的,这就是为什么有些名称如此之长,有些类型如此模糊的原因。

web-sys 中的特性 (features)

web-sys crate 中启用了所有特性可能会给 Wasm 应用程序增加很多冗余。为了解决这个问题,大多数类型都是通过启用 features 进行控制的,这样你只需要包含你的应用程序所需的类型。Yew 启用了 web-sys 的几个特性,并在其公共 API 中公开了一些类型。你通常需要自行将 web-sys 添加为依赖项。

web-sys 中的继承

模拟继承部分,你可以了解到 Rust 通常提供了一种模拟 JavaScript 中继承的方法。这在 web-sys 中非常重要,因为了解一个类型上有哪些方法意味着了解它的继承。

这一部分将查看一个特定的元素,并使用 Rust 调用 Deref::deref 列出其继承,直到该值为 JsValue

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 继承

NodeRef 中的 Node

Yew 使用 NodeRef 来提供一种方式来保留由 html! 宏创建的 Node 的引用。NodeRef 中的 Node 指的是 web_sys::NodeNodeRef::get 方法将返回一个 Option<Node> 值,但是,在 Yew 中,大多数情况下,您希望将此值转换为特定元素,以便使用其特定方法。如果存在,可以使用 JsCastNode 值进行转换,但是 Yew 提供了 NodeRef::cast 方法来执行此转换,以方便使用,因此您不一定需要为 JsCast 特性包含 wasm-bindgen 依赖项。

下面的两个代码块本质上是相同的,第一个使用 NodeRef::cast,第二个使用 JsCast::dyn_intoNodeRef::get 返回的 web_sys::Node 上。

use web_sys::HtmlInputElement;
use yew::NodeRef;

fn with_node_ref_cast(node_ref: NodeRef) {
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
// 在这里处理 HtmlInputElement
}
}

JavaScript 重构为 Rust 的示例

这一节展示了如何将与 Web API 交互的 JavaScript 代码示例重写为 Rust 中的 web-sys

JavaScript 示例

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 示例可以这样实现:

[dependencies]
wasm-bindgen = "0.2"

[dependencies.web-sys]
version = "0.3"
# 需要启用所有我们想要使用的 web-sys 特性!
features = [
"console",
"Document",
"HtmlElement",
"MouseEvent",
"DomRect",
]
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 以在 html! 宏中使用,因此示例将使用这种方法,而不是完全复制上面的方法:

[dependencies.web-sys]
version = "0.3"
# 我们需要启用 `DomRect` 特性以使用 `get_bounding_client_rect` 方法。
features = [
"console",
"HtmlElement",
"MouseEvent",
"DomRect",
]

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。

补充依赖库清单