197605d8-d36f-4347-9fba-e1fa2915aebc_1746803106274269925_origin~tplv-a9rns2rl98-image-qvalue.jpeg

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_iddocument.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_htmlset_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中,WindowDocument是两个非常重要的对象 。

Window对象代表浏览器的窗口,通过它可以访问浏览器的各种功能,比如获取当前页面的 URL、设置定时器等;

Document对象则代表当前加载的 HTML 文档,通过它可以对文档中的元素进行操作 。

获取WindowDocument对象的方法如下:

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进行调试 。

WindowDocument对象的使用,更为操作浏览器窗口和文档提供了便利 。

results matching ""

    No results matching ""