跳至主要内容
版本:0.21

web-sys

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

web-sys 中的功能

启用所有功能的 web-sys crate 会给 Wasm 应用程序增加很多负担。为了解决此问题,大多数类型都是功能门控的,以便你只包含应用程序所需的类型。Yew 启用了 web-sys 中的几个功能,并在其公共 API 中公开了一些类型。你通常需要自己将 web-sys 添加为依赖项。

web-sys 中的继承

模拟继承部分 中,你可以阅读 Rust 通常如何提供一种方法来模拟 JavaScript 中的继承。这在 web-sys 中非常重要,因为了解类型中有哪些可用方法意味着了解其继承。

本部分将查看一个特定元素,并通过调用 Deref::deref 来列出其在 Rust 中的继承,直到值变为 JsValue

use std::ops::Deref;
use web_sys::{
Element,
EventTarget,
HtmlElement,
HtmlTextAreaElement,
Node,
};

fn inheritance_of_text_area(text_area: HtmlTextAreaElement) {
// HtmlTextAreaElement is <textarea> in html.
let html_element: &HtmlElement = text_area.deref();

let element: &Element = html_element.deref();

let node: &Node = element.deref();

let event_target: &EventTarget = node.deref();

// Notice we have moved from web-sys types now into built-in
// JavaScript types which are in the js-sys crate.
let object: &js_sys::Object = event_target.deref();

// Notice we have moved from js-sys type to the root JsValue from
// the wasm-bindgen crate.
let js_value: &wasm_bindgen::JsValue = object.deref();

// Using deref like this means we have to manually traverse
// the inheritance tree, however, you can call JsValue methods
// on the HtmlTextAreaElement type.
// The `is_string` method comes from JsValue.
assert!(!text_area.is_string());

// empty function just to prove we can pass HtmlTextAreaElement as a
// &EventTarget.
fn this_function_only_takes_event_targets(targets: &EventTarget) {};

// The compiler will walk down the deref chain in order to match the types here.
this_function_only_takes_event_targets(&text_area);

// The AsRef implementations allow you to treat the HtmlTextAreaElement
// as an &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,第二个在 NodeRef::get 返回的 web_sys::Node 上使用 JsCast::dyn_into

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

fn with_node_ref_cast(node_ref: NodeRef) {
if let Some(input) = node_ref.cast::<HtmlInputElement>() {
// do something with HtmlInputElement
}
}

JavaScript 示例到 Rust

本节演示了如何使用 Rust 中的 web-sys 重写与 Web API 交互的 JavaScript 代码的示例。

JavaScript 示例

document.getElementById('mousemoveme').onmousemove = (e) => {
// e = Mouse event.
var rect = e.target.getBoundingClientRect()
var x = e.clientX - rect.left //x position within the element.
var y = e.clientY - rect.top //y position within the element.
console.log('Left? : ' + x + ' ; Top? : ' + y + '.')
}

web-sys 示例

仅使用 web-sys,上述 JavaSciprt 示例可以这样实现

[dependencies]
wasm-bindgen = "0.2"

[dependencies.web-sys]
version = "0.3"
# We need to enable all the web-sys features we want to use!
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());

// we now need to save the `mousemove` Closure so that when
// this event fires the closure is still in memory.

此版本冗长得多,但您可能会注意到部分原因是由于故障类型提醒我们,其中一些函数调用具有必须保持的不变性,否则会导致 Rust 中出现恐慌。冗长的另一部分是对 JsCast 的调用,以转换为不同的类型,以便您可以调用其特定方法。

Yew 示例

在 Yew 中,您主要将在 html! 宏中使用 Callback,因此该示例将使用此方法,而不是完全复制上述方法

[dependencies.web-sys]
version = "0.3"
# We need to enable the `DomRect` feature to use the
# `get_bounding_client_rect` method.
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 甚至强类型系统设计的,这是社区箱提供对 web-sys 的抽象以提供更多惯用 Rust API 的地方。

外部库页面