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。

[補充依賴庫清單](/community/external-libs)