Yew学习笔记

本文介绍了Rust的Yew框架,用于创建前端页面,通过HTML宏生成DOM,以及如何使用web-sys库进行Rust与JavaScript的交互,包括事件处理、类型转换和继承机制。还提到了wasm-bindgen在模拟JavaScript继承中的作用,并给出了一些代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Yew是一个Rust/Wasm框架,用来创建前端页面。

基本web技术

使用html!宏可以写HTML,这个宏的内部会将其转化为代表DOM的Rust代码:

use yew::prelude::*;

let my_header: Html = html! {
    <img src="img_girl.jpg" alt="Girl in a jacket" width="500" height="600" />
};

html!中可以使用大括号来嵌入周围的变量

use yew::prelude::*;

let header_text = "Hello world".to_string();
let header_html: Html = html! {
    <h1>{header_text}</h1>
};

let count: usize = 5;
let counter_html: Html = html! {
    <p>{"My age is: "}{count}</p>
};

let combined_html: Html = html! {
    <div>{header_html}{counter_html}</div>
};

这个嵌入应该是移动语义,并且主要到可以嵌入其他Html

html!比较重要的一个规则是其内部只能有一个node,如果要写多个node需要使用fragments,就是没有name的那个tag,应该只是语法上的形式:

use yew::html;

// fixed: using html fragments
html! {
    <>
        <div></div>
        <p></p>
    </>
};

Rust要使用Javascript其实是Wasm与Javascript交互,因为Rust代码要编译成Wasm。Wasm与Javascript交互的基础是wasm-bindgen这个库,直接用wasm-bindgen还是太麻烦了,js-sysweb-sys两个crate可以简化使用,js-sys提供了对Javascript原生API的绑定,web-sys则提供web相关API的绑定。一个使用web-sys的例子如下:

// 对应的JS代码为let document = window.document

use wasm_bindgen::UnwrapThrowExt;
use web_sys::window;

let document = window()
    .expect_throw("window is undefined")
    .document()
    .expect_throw("document is undefined");

wasm-bindgen有一个很重要的地方是模拟JS中的继承的,这对使用web-sys也很重要。wasm-bindgen中使用Rust的DerefAsRef两个trait来模拟继承。比如C继承自B,B继承自A,则有:

  • C可以Deref成B
  • B可以Deref成A
  • C可以AsRefB和A
  • B可以AsRefA

这种实现可以使C以&B或者&A的身份使用其对应方法。另外所有的类型有一个基类JsValue。yew docs中有一个示例讲了从一个HtmlTextAreaElement一直derefJaValue的过程,我把英文注释翻译成中文:

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中的类型deref到了js-sys中的JS原生类型
    let object: &js_sys::Object = event_target.deref();
    // 注意我们从js-sys中的JS原生类型deref到了wasm-bindgen中的根类型JsValue
    let js_value: &wasm_bindgen::JsValue = object.deref();

    // 实际使用中你并不需要像上面一样手动deref,由于Rust有自动deref规则
    // 所以HtmlTextAreaElement可以直接使用JsValue的方法
    // is_string这个方法是从JsValue继承的
    assert!(!text_area.is_string());

    // HtmlTextAreaElement作为函数参数也可以被自动deref,下面的代码可以证明
    fn this_function_only_takes_event_targets(targets: &EventTarget) {};
    this_function_only_takes_event_targets(&text_area);

    // 这是因为AsRef trait可以让我们像对待&EventTarget一样对待HtmlTextAreaElement
    let event_target: &EventTarget = text_area.as_ref();
}

下面放一个较长的例子,展示了JS代码如何使用web-sys以及yew来写

JS例子

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例子

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

yew例子

[dependencies.web-sys]
version = "0.3"
# We need to enable the `DomRect` feature in order 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之上也有其他的库封装,如MalvolioWeblogGloo等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值