Skip to content

Latest commit

 

History

History
232 lines (178 loc) · 7.97 KB

File metadata and controls

232 lines (178 loc) · 7.97 KB
title description
web-sys
web-sys crate 提供了Web API的绑定。

import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem'

web-sys crate 提供了 Web API 的绑定。 这些绑定是从浏览器 WebIDL 自动生成的,因此某些名称非常长,某些类型也很模糊。

Features in web-sys

web-sys crate 的所有功能都启用会让 Wasm 应用程序变得臃肿。为了解决这个问题,大多数类型都被 feature gate, 这样你只需要包含你的应用程序需要的类型。Yew 从 web-sys 启用了几个功能,并在其公共 API 中公开了一些类型。 你通常需要自己添加 web-sys 作为依赖项。

Inheritance in web-sys

Simulating inheritance 章节, 您可以了解 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 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();

}

Inheritance in web-sys in The wasm-bindgen Guide.

The Node in NodeRef

Yew使用 NodeRefhtml! 宏创建的 Node 提供引用的方法。 这里的 Node 是指 web_sys::NodeNodeRef::get 方法将返回 Option<Node> 值,然而,在Yew中,大多数情况下您都希望将此值转换为特定的元素,以便使用其特定方法。如果有的话, 可以使用 JsCastNode 值进行类型转换,但是Yew提供了 NodeRef::cast 方法来方便地执行此类型转换, 因此您不一定需要为 JsCast trait 包含 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
    }
}
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()) {
        // do something with HtmlInputElement
    }
}

JavaScript example to Rust

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

JavaScript example

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 example

只使用 web-sys,可以这样实现上面的 JavaScript 示例:

[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 example

在 Yew 中,多数情况下你将使用 Callbackhtml! 宏中, 因此下面的示例将使用该方法而不是完全复制上面的方法:

[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>
};

External libraries

web-sys 是与Web API进行交互的原始绑定,因此在Rust中使用时会有一些痛点,因为它并不是专门为Rust或强类型系统设计的, 这就是社区 create 发挥作用的地方,提供对 web-sys 的抽象,以提供更符合Rust语言习惯的API。

External libraries page