发布于 2026-01-05 5 阅读
0

Valerie:用 Rust 重新思考 Web 应用的缺陷 接下来是什么?重新思考状态 我正在尝试构建的解决方案:Valerie Valerie DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

Valerie:用 Rust 重新思考 Web 应用

缺陷

那么,接下来呢?

重新思考国家

我正在尝试构建的解决方案:Valerie

瓦莱丽

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

我个人尝试过并了解过各种用 Rust 编写的 Web 应用前端框架。说实话,它们都相当不错,能够很好地完成各自的任务。但它们也存在一些缺点,例如 API 的学习曲线较为陡峭,以及随着应用规模的增长,代码也会变得越来越复杂。

现在我看了看 JavaScript 的部分,觉得它很容易上手和使用。但是,不,我还是 Rust 的粉丝。

如何让事情变得更好?找出不足之处并加以改正。😁

缺陷

一种自然的UI编写方式🤔

我感觉用户界面编写缺乏自然流畅的方式。用户界面编写不应该受到语言特性等其他因素的阻碍。比如,Trait为了显示一个简单的元素(例如一个文本框),需要实现所有相关的函数Hello, World!,这感觉有点繁琐。

消息传递是解决问题的办法,但不要match让他们失望🥺

将消息与功能进行匹配,使用 amatch可能是一种额外的样板代码,我们可以努力减少它。

虚拟 DOM 🤨

目前的计算机速度足够快,使用虚拟 DOM 和不使用虚拟 DOM 之间的性能差异微乎其微,几乎可以忽略不计。但是,如果它确实会降低性能,我们可以尝试将其移除。

以数据为中心且可组合😮

构建可组合的用户界面要容易得多。为什么不加入这个功能呢?

那么,接下来呢?

我仔细研究了这一切,然后想,不如先编写一个假设的用户界面,它可能会使用一个假设的库,然后再尝试用 Rust 实现这个库。

fn ui() -> Something {
    div("Hello, World!")
}

fn run() {
    App::render(ui)
}
Enter fullscreen mode Exit fullscreen mode

我们应该如何管理变量和状态?它应该像这样,并且能够以某种方式自动更新。

fn ui() -> Something {
    let x = 0;

    div(
        x,
        button("Add 1").on_click(|| { x += 1; }),
    )
}
Enter fullscreen mode Exit fullscreen mode

那么,我们如何将数据与输入绑定在一起呢?

fn ui() -> Something {
    let text = String::new();

    div(
        text,
        input("text").bind(text),
    )
}
Enter fullscreen mode Exit fullscreen mode

从这些代码片段来看,基本功能已经基本实现。我们的计划是实现状态变量,在状态改变时更新它们,并将它们绑定到输入。现在让我们回到库的实现上来。

重新思考国家

  • 状态更新应该是异步的。就这么简单。
  • 我们的目标是实现一个消息传递系统,当值更新时,将消息传递给接收者,接收者将附加到元素上,并在必要时更新它们的值。
  • 这意味着我们不需要差异比较或任何虚拟 DOM。
  • 它还应该能够绑定到元素,并在输入更改时更新(并且还应该支持双向绑定)。
  • 它还应该是可推导的,即一个状态可以从其他状态推导出值,例如,如果状态 A 的值是从状态 B 推导出来的,那么当状态 B 更新其值时,状态 A 也应该更新其值。

我正在尝试构建的解决方案:Valerie

Valerie应运而生。Valerie 的设计理念是帮助人们使用 Rust 构建简洁的 Web 用户界面。

让我们从“Hello, World!”开始吧。

use valerie::prelude::components::*;
use valerie::prelude::*;

fn ui() -> Node {
    h1!("Hello, World!").into()
}

#[valerie(start)]
pub fn run() {
    App::render_single(ui());
}
Enter fullscreen mode Exit fullscreen mode

上述代码编译后是一个14.8KB.wasm的文件( gzip 压缩后为 6.8KB)。

我们来看看各州的情况。

fn ui() -> Node {
    let string = StateMutex::new(String::new());
    let length = StateAtomic::from(&string, |x| x.len());

    div!(
        h3!(string.clone()),
        h3!(length),
        input!("text").bind(string)
    )
    .into()
}
Enter fullscreen mode Exit fullscreen mode

这将显示一个绑定到某个状态的输入框String,该length状态的变量值来源于该string状态。当你在输入框中输入内容时,数据会自动更新。

上述代码编译后是一个44.7KB.wasm的文件( gzip 压缩后为 15.1KB)。

这里需要注意的是 ` StateAtomicA` 和 `B` StateMutex,它们分别对应实现了 `A` 和 `B` 接口的类型Copy。`A`Clone内部StateAtomic使用原子操作来实现并发,而StateMutex`B` 则使用互斥锁。

如果你想点击一个按钮,让某个计数器变量递增,该怎么办?

fn ui() -> Node {
    let count = StateAtomic::new(0);

    div!(
        h3!(count.clone()),

        button!("Click Me!")
        .on_event("click", count, |x, _| {
            *x += 1;
        })
    )
    .into()
}
Enter fullscreen mode Exit fullscreen mode

上述代码编译后是一个33.1KB.wasm的文件( gzip 压缩后为13.5KB)。

以上只是我展示的Valerie众多功能中的一部分。该图书馆目前仍处于早期开发阶段。Valerie
本身也是一座no-std图书馆。

每个 UI 组件(例如 `<div>`String或 ` div<div>`)都实现了该Componenttrait。你甚至可以将 UI 的一部分提取到一个单独的函数中,该函数返回一个 `<div>` 对象impl Component,然后在需要时重用它。

Rust 遵循零成本抽象原则,也就是说,你无需为不使用的库特性付出性能代价,而且你使用的任何特性都无法通过手工编写得到更好的实现。这意味着,如果你不使用 Valerie 库中提供的某个特性,那么它就不会出现在编译后的.wasm文件中。

对比 WASM 和 JS,浏览器接收到 JS 文件后,需要将其转换为抽象语法树 (AST),编译必要的函数并运行。这需要耗费大量资源。而 WASM 文件则不同,浏览器可以直接使用即时编译器 (JIT) 运行它,无需.wasm额外资源进行优化。因为 WASM.wasm文件是经过优化的可移植二进制文件,浏览器无需浪费资源进行优化。

感谢您阅读本文,也请您关注一下瓦莱丽

GitHub 标志 emmanuelantony2000 / valerie

用于构建 Web 应用程序的 Rust 前端框架

瓦莱丽

置信区间 执照 货物 文档 Discord

用于构建 Web 应用程序的 Rust 前端框架。

Valerie 目前仍处于非常早期的阶段,许多功能尚未推出,还有很多工作要做,欢迎您来试用。

  • 无虚拟DOM。
  • 通过遵循 MVVM 架构而不是 MVC 架构,可以更简单地创建用户界面。
  • 使用状态变量在需要时更新用户界面。
  • 编写过程中未使用任何不安全代码。

建筑学

  • 每个 UI 元素都必须实现该Component特性。
  • 页面是一个返回值的函数Node
  • 两种类型的状态变量
    • StateAtomic对于实现以下类型的类型Copy
    • StateMutex对于实现以下类型的类型Clone

设置

  • 跑步cargo new --lib some_name
  • 添加valerie到依赖项
  • 创建一个目录,并在其中static创建一个文件。index.html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="
Enter fullscreen mode Exit fullscreen mode
文章来源:https://dev.to/emmanuelantony2000/valerie-rethinking-web-apps-in-rust-4cl3