leptos - 使用 Rust 构建快速的 Web 应用程序。

Created at: 2022-08-01 04:40:30
Language: Rust
License: MIT

请注意:该框架正在积极开发中。目前,我将其保持在 0.0.x 版本的循环中,以表明它甚至还没有为 0.1.0 做好准备。正在对文档和功能进行积极的工作,API 不一定被认为是稳定的。同时,它不仅仅是一个玩具项目或概念验证,我正在积极将其用于我自己的应用程序开发。

乐浦标志

crates.io docs.rs 不和

乐浦

use leptos::*;

#[component]
pub fn SimpleCounter(cx: Scope, initial_value: i32) -> Element {
    // create a reactive signal with the initial value
    let (value, set_value) = create_signal(cx, initial_value);

    // create event handlers for our buttons
    // note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures
    let clear = move |_| set_value(0);
    let decrement = move |_| set_value.update(|value| *value -= 1);
    let increment = move |_| set_value.update(|value| *value += 1);

    // this JSX is compiled to an HTML template string for performance
    view! {
        cx,
        <div>
            <button on:click=clear>"Clear"</button>
            <button on:click=decrement>"-1"</button>
            <span>"Value: " {move || value().to_string()} "!"</span>
            <button on:click=increment>"+1"</button>
        </div>
    }
}

// Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setup
pub fn main() {
    mount_to_body(|cx| view! { cx,  <SimpleCounter initial_value=3 /> })
}

关于框架

Leptos 是一个全栈、同构的 Rust Web 框架,利用细粒度的 React 性来构建声明式用户界面。

那是什么意思?

  • 全栈:Leptos 可用于构建在浏览器中运行的应用程序(客户端渲染)、服务器(服务器端渲染),或者通过在服务器上渲染 HTML 然后在浏览器中添加交互性(水化)来构建应用程序。这包括对数据和 HTML(组件的无序流)的 HTTP 流的支持。
    Resource
    <Suspense/>
  • 同构:Leptos 提供了编写同构服务器函数的原语,即可以在客户端或服务器上以“相同形状”调用但只能在服务器上运行的函数。这意味着你可以编写仅服务器逻辑(数据库请求、身份验证等)以及将使用它的客户端组件,并调用服务器函数,就像它们在浏览器中运行一样。
  • Web:Leptos建立在Web平台和Web标准之上。路由器旨在使用Web基础知识(如链接和表单)并在其上构建,而不是想替换它们。
  • 框架:Leptos 提供了构建现代 Web 应用程序所需的大部分内容:响应式系统、模板库和可在服务器端和客户端工作的路由器。
  • 细粒度 React 性:整个框架都是从 React 式原语构建的。这允许以最小的开销提供性能极高的代码:当 React 信号的值发生变化时,它可以更新单个文本节点,切换单个类或从 DOM 中删除元素,而无需运行任何其他代码。(所以,没有虚拟 DOM!)
  • 声明式:告诉Leptos你想要页面的外观,并让框架告诉浏览器如何做。

了解更多信息

以下是一些了解有关乐浦的更多信息的资源:

nightly
注意

大多数示例都假设你正在使用 Rust。如果你处于稳定状态,请注意以下事项:

nightly

  1. 你需要在 中启用该标志:
    "stable"
    Cargo.toml
    leptos = { version = "0.0", features = ["stable"] }
  2. nightly
    启用用于访问和设置信号的函数调用语法。如果使用 ,则只需调用 、 或手动操作。查看计数器稳定示例,了解正确 API 的示例。
    stable
    .get()
    .set()
    .update()

基准

服务器端呈现

我创建了一个基准测试,将Leptos在服务器上的HTML渲染与TeraYewSycamore进行了比较。你可以在此处找到基准测试,并使用 .Leptos 渲染 HTML 的速度大致与 Tera 一样快,并且随着模板变大而扩展良好。它比类似框架完成的服务器端HTML渲染要快得多。

cargo bench

点击显示结果
ns/iter 泰拉 乐浦 紫杉 悬铃木
3 计数器 3,454 5,666 34,984 32,412
TodoMVC (no todos) 2,396 5,561 38,725 68,749
TodoMVC (1000 todos) 3,829,447 3,077,907 5,125,639 19,448,900
平均 1.08 1.65 6.25 9.36

客户端呈现

测试前端 Web 框架原始渲染性能的黄金标准是 js-framework-benchmark。官方结果将Leptos列为最快的Rust / Wasm框架,比SolidJS略慢,比流行的JS框架(如Svelte,Preact和React)快得多。

点击显示结果 js-框架-基准测试结果

常见问题

我可以将其用于本机 GUI 吗?

确定!显然,宏用于生成 DOM 节点,但你可以使用响应式系统来驱动任何使用与 DOM 相同类型的面向对象、基于事件回调的框架的 GUI 工具包。原理是相同的:

view

  • 使用信号、派生信号和备忘录来创建 React 式系统
  • 创建图形用户界面小部件
  • 使用事件侦听器更新信号
  • 创建效果以更新 UI

我整理了一个非常简单的 GTK 示例,以便你了解我的意思。

这与红豆杉/红豆杉有何不同?

从表面上看,这些库可能看起来很相似。当然,Yew 是用于 Web UI 开发的最成熟的 Rust 库,并且拥有庞大的生态系统。Dioxus 在很多方面都很相似,都深受 React 的启发。以下是乐浦和这些框架之间的一些概念差异:

  • VDOM 与细粒度:Yew 建立在虚拟 DOM (VDOM) 模型之上:状态更改会导致组件重新渲染,从而生成新的虚拟 DOM 树。Yew 将其与以前的 VDOM 进行了区分,并将这些补丁应用于实际的 DOM。 每当状态更改时,组件函数都会重新运行。乐浦采取了完全不同的方法。组件运行一次,创建(并返回)实际的 DOM 节点,并设置一个 React 式系统来更新这些 DOM 节点。
  • 性能:这具有巨大的性能影响:Leptos 在创建和更新 UI 方面都比 Yew 快得多
  • 心智模型:采用细粒度的 React 性也倾向于简化心智模型。没有令人惊讶的组件重新渲染,因为没有重新渲染。你的应用可以根据对你的应用有意义的内容划分为多个组件,因为它们对性能没有影响。

这与梧桐树有何不同?

从概念上讲,这两个框架非常相似:因为两者都建立在细粒度的 React 性之上,所以大多数应用程序最终在两者之间看起来非常相似,Sycamore或Leptos应用程序看起来都很像SolidJS应用程序,就像Yew或Dioxus看起来很像React一样。

有一些实际差异会产生显着差异:

  • 成熟:Sycamore显然是一个更成熟,更稳定的库,具有更大的生态系统。
  • 模板:Leptos 使用类似 JSX 的模板格式(基于 syn-rsx 构建)作为其宏。Sycamore提供了自己的模板DSL或构建器语法的选择。
    view
  • 模板节点克隆:Leptos 的宏编译为一个静态的 HTML 字符串和一组如何分配其 React 式值的指令。这意味着在运行时,Leptos 可以克隆节点,而不是调用来创建 DOM 节点。这是一种明显更快的渲染组件的方法。
    view
    <template>
    document.createElement()
  • 读写隔离:Leptos 和 Solid 一样,鼓励信号获取器和设置器之间的读写隔离,因此你最终可以使用元组访问信号,例如(如果你愿意,或者如果你的 API 更方便,你可以使用 create_rw_signal 来提供统一的读/写信号。
    let (count, set_count) = create_signal(cx, 0);
  • 信号是函数:在 Leptos 中,你可以调用一个信号来访问它,而不是调用一个特定的方法(所以,而不是 ) 这创造了一个更一致的心智模型:访问一个 React 值总是调用一个函数的问题。例如:
    count()
    count.get()
let (count, set_count) = create_signal(cx, 0); // a signal
let double_count = move || count() * 2; // a derived signal
let memoized_count = create_memo(cx, move |_| count() * 3); // a memo
// all are accessed by calling them
assert_eq!(count(), 0);
assert_eq!(double_count(), 0);
assert_eq!(memoized_count(), 0);

// this function can accept any of those signals
fn do_work_on_signal(my_signal: impl Fn() -> i32) { ... }
  • 信号和示波器是“静态的”:Leptos 和 Sycamore 都减轻了闭包(特别是事件侦听器)中移动信号的痛苦,以避免 Rust UI 代码中非常熟悉的。Sycamore 通过使用凹凸分配将其信号的生命周期与其作用域联系起来来实现这一点:由于引用是 ,可以移动到闭包中。Leptos 通过使用竞技场分配和传递索引来实现这一点:像 、 这样的类型实际上是将索引包装到竞技场中。这意味着示波器和信号都在乐浦中,这意味着它们可以很容易地移动到闭包中,而不会增加生命周期的复杂性。
    Copy
    { let count = count.clone(); move |_| ... }
    Copy
    &'a Signal<T>
    ReadSignal<T>
    WriteSignal<T>
    Memo<T>
    Copy
    'static