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
中非常重要,因為瞭解類型中的有哪些方法可用,表示瞭解它的繼承。
本部分將檢視特定元素,並使用 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();
}
在 wasm-bindgen
指南中的 web-sys
中繼承.
NodeRef
中的 Node
Yew 使用 NodeRef
提供一種方式,讓由 html!
巨集建立的 Node
保持參考。NodeRef
的 Node
部分是指 web_sys::Node
。NodeRef::get
方法會傳回 Option<Node>
值,但大部分時候,你會希望在 Yew 中將這個值轉換為特定元素,讓你能夠使用它特定的方法。這個轉換可以使用 Node
值上的 JsCast
來執行,只要有這個值時,但 Yew 提供 NodeRef::cast
方法來執行這個轉換以方便,也讓你不一定要包含 JsCast
特質的 wasm-bindgen
相依性。
以下兩個程式區塊基本上執行相同的事情,第一個使用 NodeRef::cast
,第二個使用 JsCast::dyn_into
在從 NodeRef::get
傳回的 web_sys::Node
上。
- 使用 NodeRef::cast
- 使用 NodeRef::get
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 範例轉換為 Rust
本部分說明與 Web API 互動的 JavaScript 程式碼如何使用 Rust 重新以 web-sys
撰寫的範例。
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
,上面的 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 範例
在 Yew 中,您將大部份建立 Callback
在 html!
巨集中使用,因此範例將使用此方法,而不是完全複製上述方法
[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。