Rust前端框架Yew:写一个颜色选择器

201 阅读3分钟

WX20230708-230059@2x.png

之前有写过按钮、评分组件,这次选择写一个更复杂点,于是就选了颜色选择器,之前的组件都是单一的,这次的颜色选择器组件则是好几个子组件构成的,实现部分依旧是参考Element UI的相关组件,功能方面基本上实现了跟Element UI原组件一样的功能,预设颜色没有实现,另外因为输入控件是个相对比较复杂的组件,目前使用文本来替代。

WX20230708-230254@2x.png

生命周期

组件的生命周期,我们可以看下Yew相关的源码,详细见源码链接

pub trait BaseComponent: Sized + 'static {
    /// The Component's Message.
    type Message: 'static;

    /// The Component's Properties.
    type Properties: Properties;

    /// Creates a component.
    fn create(ctx: &Context<Self>) -> Self;

    /// Updates component's internal state.
    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool;

    /// React to changes of component properties.
    fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool;

    /// Returns a component layout to be rendered.
    fn view(&self, ctx: &Context<Self>) -> HtmlResult;

    /// Notified after a layout is rendered.
    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool);

    /// Notified before a component is destroyed.
    fn destroy(&mut self, ctx: &Context<Self>);

    /// Prepares the server-side state.
    fn prepare_state(&self) -> Option<String>;
}

之前用的最多的是createupdateview这几个,这次新使用了rendered,这个提供一个是否是第一次渲染。

节点引用

fn view(&self, ctx: &Context<Self>) -> Html {
    html! {
        <div class="el-color-hue-slider is-vertical">
            <div ref={&self.bar_ref} class="el-color-hue-slider__bar" onclick={ctx.link().callback(|e|{
                Msg::OnBarClick(e)
            })}></div>
            <div ref={&self.thumb_ref} class="el-color-hue-slider__thumb" style={format!("left: {}px; top: {}px", self.thumb_left, self.thumb_top)}>
            </div>
        </div>
    }
}

之前使用Vue的时候,也经常使用这个。这次实现滑动条时,需要在更新时使用某个节点的引用,一开始使用了笨重的方式,通过ID来获取,但是很有可能ID在全局中会重复。

let document = web_sys::window().unwrap().document().unwrap();
let thumb = document
    .query_selector("#thumb")
    .unwrap()
    .unwrap()
    .dyn_ref::<HtmlElement>()
    .unwrap()
    .clone();

使用节点引用后则比较简单。

let thumb = self.thumb_ref.cast::<HtmlElement>().unwrap();

模块

一开始所有代码都在根目录文件夹下,文件多了自然不能这样,比如颜色选择器组件就有好几个自组件。

image.png

会发现都有mod.rs的文件,先看下components下的。

pub mod yew_color_hue_slider;
pub mod yew_picker_dropdown;
pub mod yew_sv_panel;
pub mod yew_color_alpha_slider;

比如yew_picker_dropdown.rs使用同目录下的模块。

use super::yew_color_hue_slider::YewColorHueSlider;
use super::yew_sv_panel::YewSvPanel;
use super::yew_color_alpha_slider::YewColorAlphaSlider;

再看下yew_color_picker目录下的mod.rs

mod components;
pub mod yew_color_picker;

会发现第一个没有加pub,这样的话yew_color_picker以外就无法使用了,可以保证这些组件只是yew_color_picker在使用了。

关于这方面的内容,我感觉《Rust程序设计》这本书比《Rust权威指南》讲解的明了,更多的可以参阅此书。习惯了写Java之类,一开始确实发现有点麻烦,但我觉得这些似乎可以在IDE下得到支持。

第三方库

调试输出

目前主要使用了gloo-console这个库,再结合format!宏,非常的好用。

log!(format!("r: {}, g: {}, b:{}", rgb.0, rgb.1, rgb.2));

Math

实现中有大量加减乘除,使用最多的还是js_sys::Math这个模块。

let mut left = e.client_x() as f64 - rect.left() as f64;
left = js_sys::Math::max(thumb.offset_width() as f64 / 2.0, left);
left = js_sys::Math::min(
    left,
    rect.width() as f64 - thumb.offset_width() as f64 / 2.0,
);

对比Vue

实现上主要参考Element UI的源码,但是YewVue还是有不少不同的地方,还是有些不同。

之前习惯了Vuev-model,但是Yew没有这样东西,所以每次组件的值变化,总是会通过on_change这样的回调来通知父组件。

Vue提供了computedwatch这类,但是Yew中没有这些。

总的来说Yew只是提供了基本的东西,一些附加的东西还是需要自己去实现。

总结

相关的源码都在这里,喜欢的可以自己研究下。