《Rust Wasm 探索之旅:从入门到实践》系列一:当 Rust 遇见 WebAssembly:Wasm 与 Rust 生态初探(入门篇)
《Rust Wasm 探索之旅:从入门到实践》系列二:Rust+Wasm利器:用wasm-pack引爆前端性能!
《Rust Wasm 探索之旅:从入门到实践》系列三:Rust & WASM 之
wasm-bindgen
基础:让 Rust 与 JavaScript 无缝对话《Rust Wasm 探索之旅:从入门到实践》系列四:Rust & WASM 之
wasm-bindgen
进阶:解锁 Rust 与 JS 的复杂数据交互秘籍《Rust Wasm 探索之旅:从入门到实践》系列五:Rust & WebAssembly:探索js-sys的奇妙世界
《Rust Wasm 探索之旅:从入门到实践》系列六:Rust & WebAssembly:web-sys 开启 DOM 操作新篇
Rust & WebAssembly:web-sys 开启 DOM 操作新篇
一、什么是 web-sys
web-sys
是一个 Rust 库,它为 Web API 提供了原始绑定,允许开发者在 Rust 中直接调用浏览器的原生 API。
这些 API 涵盖了 DOM(文档对象模型)、Console(控制台)、Window(窗口)、Document(文档) 等多个方面,为构建丰富的 Web 应用提供了基础支持。
但是,web-sys
的独特之处在于,它通过 Rust 的类型系统,为这些操作提供了类型安全的保障,大大减少了运行时错误的可能性。
二、启用特定特性(features)
Web API 非常庞大,如果全部引入,会导致生成的 Wasm 体积过大。web-sys
采用了特性(feature)机制,允许我们按需引入所需的 Web API 绑定 ,从而减小 Wasm 体积。
在Cargo.toml
文件中,我们可以这样启用特定特性:
[dependencies.web-sys]
version = "0.3"
features = ["Document", "HtmlElement", "EventTarget"]
比如,当我们需要使用console.log
进行调试时,就需要启用console
特性:
[dependencies.web-sys]
version = "0.3"
features = ["console"]
通过这种方式,我们可以精确控制引入的 Web API,有效优化项目的体积和性能。
💡 小贴士: features
里的每一项都对应一个 Web API 接口。比如,想用 console.log
就得加 'console'
,想操作 DOM 元素就得加 'Element'
, 'Document'
等。
三、基本 DOM 操作
获取 DOM 元素
在web-sys
中,我们可以使用document.get_element_by_id
和document.query_selector
来获取 DOM 元素。get_element_by_id
通过元素的 id 来获取,而query_selector
则支持更灵活的 CSS 选择器语法。
示例代码如下:
use web_sys::{Document, HtmlElement, Element, console};
#[wasm_bindgen]
pub fn get_elements() {
let window = web_sys::window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
// 通过id获取元素
if let Some(_elem) = document.get_element_by_id("my-id") {
// 处理获取到的元素
console::log_1(&"Element found by ID".into());
} else {
// 处理未找到元素的情况
}
// 使用query_selector获取元素
if let Ok(Some(_elem)) = document.query_selector("#my-id") {
// 处理获取到的元素
console::log_1(&"Element found by querySelector".into());
} else {
// 处理选择器错误或未找到元素的情况
}
}
创建和添加 DOM 元素
使用document.create_element
可以创建新的 DOM 元素,然后使用parent_element.append_child
将新元素添加到指定的父元素下。
示例代码:
use web_sys::{Document, HtmlElement, Element};
#[wasm_bindgen]
pub fn create_and_add_element() {
let window = web_sys::window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
// 创建一个新的div元素
let new_div = document.create_element("div").expect("Failed to create div");
// 添加文本内容
new_div.set_text_content(Some("This is a new div"));
// 获取body元素作为父元素
if let Some(body) = document.body() {
body.append_child(&new_div).expect("Failed to append div to body");
}
}
修改元素属性和内容
通过set_attribute
方法可以修改元素的属性,使用set_inner_html
或set_text_content
可以修改元素的内容。
示例代码:
use web_sys::{Document, HtmlElement, Element};
#[wasm_bindgen]
pub fn modify_element() {
let window = web_sys::window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
if let Some(elem) = document.get_element_by_id("my-id") {
// 修改属性
elem.set_attribute("class", "new-class").expect("Failed to set attribute");
// 修改内容
elem.set_inner_html("<p>New content</p>");
// 或者使用 set_text_content
// elem.set_text_content(Some("New text content"));
}
}
处理事件监听
使用add_event_listener_with_callback
方法可以为 DOM 元素添加事件监听器。下面以点击事件为例:
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlElement, Element, MouseEvent};
#[wasm_bindgen]
pub fn add_event_listener() {
let window = window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
let p = document.get_element_by_id("my-id").unwrap();
// 1. 创建一个闭包作为事件回调函数
let onclick_callback = Closure::<dyn FnMut(_)>::new(move |event: MouseEvent| {
console::log_1(&"Button was clicked!".into());
console::log_1(&event.into());
});
// 2. 将 p 元素转换为 EventTarget
let target: &web_sys::EventTarget = p.dyn_ref::<web_sys::EventTarget>()
.expect("element should be an event target");
// 3. 添加事件监听
target.add_event_listener_with_callback(
"click",
onclick_callback.as_ref().unchecked_ref(),
);
// 4. 关键!忘记闭包的内存,否则它将在函数结束时被回收
onclick_callback.forget();
}
四、使用 console.log 等进行调试
开发过程中,调试是必不可少的环节。在web-sys
中,我们可以使用console.log
等函数来输出调试信息 。首先,需要在Cargo.toml
中启用console
特性,然后在代码中引入console
模块 。
示例代码:
use web_sys::{Document, HtmlElement, Element, console};
#[wasm_bindgen]
pub fn debug_info() {
console::log_1(&"Debug message".into());
let num = 42;
console::log_2(&"The number is".into(), &num.into());
console::log_1(&"Hello from Rust!".into()); // 对应 console.log(value)
let some_value = JsValue::from(42);
console::log_2(&"The answer is: ".into(), &some_value); // 对应 console.log(v1, v2)
}
在上述代码中,console::log_1
用于输出一个值,console::log_2
用于输出两个值,以此类推,最多可以输出 7 个值。
通过这些函数,可以方便地在浏览器控制台中查看变量的值和程序的执行状态 ,帮助我们快速定位和解决问题。
五、Window 和 Document 对象的使用
在web-sys
中,Window
和Document
是两个非常重要的对象 。
Window
对象代表浏览器的窗口,通过它可以访问浏览器的各种功能,比如获取当前页面的 URL、设置定时器等;
Document
对象则代表当前加载的 HTML 文档,通过它可以对文档中的元素进行操作 。
获取Window
和Document
对象的方法如下:
use web_sys::{window, Document, EventTarget, console};
#[wasm_bindgen]
pub fn window_and_document() {
let window = window().expect("no global window exists");
let document = window.document().expect("should have a document on window");
// 使用Window对象
let current_url = window.location().href().expect("Failed to get URL");
console::log_1(&format!("Current URL: {}", current_url).into());
// 使用Document对象
let title = document.title();
console::log_1(&format!("Page Title: {}", title).into());
document.set_title("New Title from Rust!");
let set_time_out_callback = Closure::<dyn FnMut()>::new(move || {
console::log_1(&"3秒后执行".into());
});
// 定时任务
window.set_timeout_with_callback(set_time_out_callback.as_ref().unchecked_ref());
set_time_out_callback.forget();
}
在上述代码中,通过window()
函数获取Window
对象,再通过window.document()
方法获取Document
对象 。接着,使用window.location().href()
获取当前页面的 URL,使用document.title()
获取页面的标题,并通过console.log
输出 。
注意 要在 Cargo.toml
中启用 Location
等特性,才能使用 location()
等函数。
六、总结
核心回顾:
web-sys
是 Web API 的 Rust 绑定。- 通过
features
按需加载,保持 Wasm 轻量。 - DOM 操作逻辑与 JavaScript 类似,但有 Rust 的类型安全加持。
- 使用
Closure
来处理事件监听。
通过本文的介绍,我们了解了web-sys
库在 Rust 中操作浏览器 DOM 的重要性和优势。
web-sys
库不仅提供了对 Web API 的原始绑定,还通过特性机制优化了 Wasm 体积 。
借助它,就能够安全、高效地进行 DOM 操作,如获取、创建、修改元素以及处理事件监听 ,同时还能方便地使用console.log
进行调试 。
Window
和Document
对象的使用,更为操作浏览器窗口和文档提供了便利 。