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)
}
我们应该如何管理变量和状态?它应该像这样,并且能够以某种方式自动更新。
fn ui() -> Something {
let x = 0;
div(
x,
button("Add 1").on_click(|| { x += 1; }),
)
}
那么,我们如何将数据与输入绑定在一起呢?
fn ui() -> Something {
let text = String::new();
div(
text,
input("text").bind(text),
)
}
从这些代码片段来看,基本功能已经基本实现。我们的计划是实现状态变量,在状态改变时更新它们,并将它们绑定到输入。现在让我们回到库的实现上来。
重新思考国家
- 状态更新应该是异步的。就这么简单。
- 我们的目标是实现一个消息传递系统,当值更新时,将消息传递给接收者,接收者将附加到元素上,并在必要时更新它们的值。
- 这意味着我们不需要差异比较或任何虚拟 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());
}
上述代码编译后是一个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()
}
这将显示一个绑定到某个状态的输入框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()
}
上述代码编译后是一个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文件是经过优化的可移植二进制文件,浏览器无需浪费资源进行优化。
感谢您阅读本文,也请您关注一下瓦莱丽。
瓦莱丽

用于构建 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="…
文章来源:https://dev.to/emmanuelantony2000/valerie-rethinking-web-apps-in-rust-4cl3