Rust Yew应用开发的事件初探

在Rust的世界中有一个叫Yew的框架,它借鉴了React的思想。我的React代码也写了不少,今天就聊一下我个人对Yew应用开发中事件相关部分的体验。

我的也是才开始学习Rust和Yew,说得不对的地方还请大家多多指教。

下面的例子涉及到3个组件

ParentComponent组件

Button组件

<button/>原生html元素

也就是说,在ParentComponent组件中放置了Button组件,这个Button组件其实就是对<button/>的一个封装。这里的事件传递就是

<button/> -> Button -> ParentComponent

先从Button组件中的点击事件说起。

按钮事件定义如下

rust 复制代码
use yew::{Properties, Callback, MouseEvent};


#[derive(Properties, PartialEq)]
pub struct Props {
  #[prop_or_default]
  pub onclick: Callback<MouseEvent>,
}

这里上面关于事件的onclick,虽然就2行代码,但这里面的信息量比较大,我将为大家逐一分析,以能知其所以然。

Properties

用于定义组件的参数,和React一样,当组件的参数更新时,Yew框架会自动更新组件。

在Yew框架中,通过结构体(struct)来定义组件的参数。在这个结构体中,你可以定义各种类型的字段,这些字段对应的就是组件的参数。

Properties本身是一个tarit,但并不需要我们开发人员来实现这个trait。Yew框架通过Rust的derive功能,自动将Properties的实现和你定义的结构体结合起来。

在结构体的字段中,就包含了我们要讨论的事件Callback。

也就是说,我们的事件就可以定义成下面这个样子。

rust 复制代码
#[derive(Properties)]
pub struct Props {
  pub onclick: Callback<()>,
}

PartialEq

和Properties一样,PartialEq也是一个trait,如果大家结合React的组件更新逻辑,估计能够猜得出来它的用意。是的,它就是Yew框架用来比较属性值,看其是否有更新。不过你完全不必担心自己忘记加上这个trait,因为编译器的错误报告会提醒你。这也是我喜欢Rust的地方。

Callback

用于定义组件暴露出来的事件。它的声明形式如下:

rust 复制代码
pub struct Callback<IN, OUT = ()> { /* private fields */ }

当我们定义一个空事件时,代码就是这样:

rust 复制代码
pub onclick: Callback<()>

在Yew应用开发中,通常用Callback<>来定义事件,因为Yew框架本身用Callback类型包装了html元素的事件。使用Callback类型来定义事件比较方便和安全。

prop_or_default

prop_or_default其实不是必须的。如果你不为你的字段添加这个attribute 宏,就必须在使用这个组件的地方为它设置值,否者会编译报错。这对组件的使用者来说显然不太友好。

因此,这里我们使用到了prop_or_default,它可以帮助Yew框架,在运行时,如果没有为事件字段赋值,系统会为其计算推导出默认值。

在Yew框架中,除了prop_or_default,还有prop_or(value)和prop_or_else(function)。

prop_or_default: 使用类型的默认值

prop_or(value): 使用给定的默认值

prop_or_else(function): 通过函数来获取默认值

MouseEvent

yew::MouseEvent实际上是从web_sys中导出的,它包含了鼠标当前的位置信息。具体可以参考https://docs.rs/web-sys/0.3.60/web_sys/struct.MouseEvent.html

之所以把MouseEvent提出来,是为了让大家对Yew中的Html的事件参数有一个初步的了解。

rust 复制代码
pub use web_sys::AnimationEvent;
pub use web_sys::DragEvent;
pub use web_sys::ErrorEvent;
pub use web_sys::Event;
pub use web_sys::FocusEvent;
pub use web_sys::InputEvent;
pub use web_sys::KeyboardEvent;
pub use web_sys::MouseEvent;
pub use web_sys::PointerEvent;
pub use web_sys::ProgressEvent;
pub use web_sys::SubmitEvent;
pub use web_sys::TouchEvent;
pub use web_sys::TransitionEvent;
pub use web_sys::UiEvent;
pub use web_sys::WheelEvent;

上面说了这么多关于Yew事件定义,接下来看看如何从原生的<button/> html元素上获取事件吧。

使用方式和React几乎是一样的,开发过React项目的同学是不是觉得很亲切啊。

rust 复制代码
  <button style={danger_style} onclick={props.onclick.clone()}>
    {props.text.clone()}
  </button>

Button.rs的完整代码如下:

rust 复制代码
use yew::{function_component, html, Callback, Html, MouseEvent, Properties};

#[derive(PartialEq)]
pub enum DisplayType {
    Default,
    Danger,
}

#[derive(Properties, PartialEq)]
pub struct Props {
    #[prop_or_default]
    pub text: String,
    #[prop_or(DisplayType::Default)]
    pub display_type: DisplayType,
    #[prop_or_default]
    pub onclick: Callback<MouseEvent>,
}

static default_style: &str = "..."; //隐藏不必要代码
static danger_style: &str = "..."; //隐藏不必要代码

#[function_component]
pub fn Button(props: &Props) -> Html {
    match props.display_type {
        DisplayType::Danger => html! {
            <button style={danger_style} onclick={props.onclick.clone()}>
              {props.text.clone()}
            </button>
        },
        DisplayType::Default => html! {
            <button style={default_style} onclick={props.onclick.clone()}>
              {props.text.clone()}
            </button>
        },
    }
}

在上面的Button.rs中,我们创建了组件Button,并为其定义了事件onclick,下面就来看如何消费这个事件。

这里我想稍作停顿。我们知道Yew借鉴了React的思想,可以看到两者间很多的相似之处。但是毕竟Yew是基于Rust语言开发的,而React(这里特指ReactJs)是基于Javascript开发的。前者是静态编译语言,后者是动态语言。

因此,在Reat中,如何要处理Button中的onclick事件,代码应该像下面这个样子:

javascript 复制代码
<Button onclick={()=>console.log('clicked')}/>

但是在Yew中,代码就没有简单了。

没关系,只要知其所以然,其实Yew中的事件处理也很简单。

在Yew应用开发中,处理事件的步骤如下:

  1. 定义事件枚举
  2. 将事件枚举绑定到要处理的组件事件上
  3. 在当前组件的update方法中处理事件

下面的代码用struct组件为例来展示如何处理Button上的onclick事件。

定义事件枚举

rust 复制代码
use yew::{ Component }
enum ParentComponentMsg {
    Toggle,
}

pub struct ParentComponent;

impl Component for ParentComponent {
    type Message = ParentComponentMsg;
}

在上面的代码中,按照Yew的惯例,将事件枚举的名称定义为"组件名称"+Msg,建议大家也遵守这个惯例。

然后不要忘记,对于初学者来说很容易忘记下面这行代码:

rust 复制代码
type Message = ParentComponentMsg;

这行代码是告诉编译器,ParentComponent使用的事件枚举的类型。如果没有这行代码,或者这行代码是"type Message = ();",在下一个步骤上,编译器会报错。

bash 复制代码
    the trait `std::convert::From<components::base::parentComponent::ParentComponentMsg>` is not implemented for `()`

将事件枚举绑定到要处理的组件事件上

这里我们要处理Button组件的onclick事件,代码如下:

rust 复制代码
    fn view(&self, ctx: &Context<Self>) -> Html {
        let onclick = ctx.link().callback(|_| ParentComponentMsg::Toggle);
 
        html! {
            <div class="modal">
               <Button text="点击" onclick={onclick}/>
            </div>
        }
    }

上面的参数中,Context的出现频率很高,它用于获取当前组件的属性值,以及处理事件相关的处理。后面有机会再展开说说。

在当前组件的update方法中处理事件

rust 复制代码
    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            ParentComponentMsg::Toggle => {
                log!("clicked");
                // 可以在这里修改self上的属性,即改变组件本身的状态,来触发新的渲染。这就是react的做法了。
            }
        }
        true
    }

让我再来夸夸Rust,它利用它的mut关键字来告诉编译器,这个对象是可修改的。如果没有mut关键字,但有修改对象属性的代码,编译器会直接报错。这一点在ReactJs中是做不到的。在ReactJs中,我们只能严格遵守只能通过setXXX来修改状态,但如果你鬼使神差地直接修改了状态值,一般情况下好像也没有报错(我不确定某些eslint插件是否具有这个功能)。

相比于ReactJs,要处理一个事件要多些这么多的代码,是有点不方便,但是如果你的代码写错了,编译器会报错,这里总算挽回了一点面子。

好了,关于《Rust Yew应用开发的事件初探》我就写在这里了。这真的是一个初探,里面的见解比较粗浅,因为我也是边学边用。我要去写我的项目去了,有什么新发现我再来和大家分享。

有不对的地方还请大家留言指教。

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax