leptos:使用Rust构建快速Web应用的开源工具


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

特点:

  • 全堆栈:Leptos可以用来构建在浏览器(客户端渲染)、服务器(服务器端渲染)中运行的应用程序,或者通过在服务器上渲染HTML,然后在浏览器中添加交互性(服务器端渲染与氢化)。这包括支持数据(资源)和HTML的HTTP流(<Suspense/>组件的顺序外或顺序内流)。
  • 同构性:Leptos提供了编写同构服务器函数的基元,即可以在客户端或服务器上以 "相同的形状 "调用的函数,但只在服务器上运行。这意味着你可以在客户端组件旁边编写你的服务器专用逻辑(数据库请求、认证等),并像在浏览器中运行一样调用服务器功能,而不需要创建和维护一个单独的REST或其他API。
  • Web:Leptos是建立在Web平台和Web标准之上的。路由器被设计为使用Web的基本要素(如链接和表单)并建立在它们之上,而不是试图取代它们。
  • 框架:Leptos提供了你构建现代Web应用所需的大部分东西:一个反应式系统、模板库和一个在服务器和客户端都能工作的路由器。
  • 细粒度的反应性:整个框架是由反应式基元构建的。这允许以最小的开销实现极高性能的代码:当一个反应式信号的值发生变化时,它可以更新一个文本节点,切换一个类,或从DOM中删除一个元素,而无需运行任何其他代码。(所以,没有虚拟DOM的开销!)。
  • 声明性的:告诉Leptos你想让页面看起来如何,并让框架告诉浏览器如何做。

代码:

use leptos::*;

#[component]
pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView {
    // 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);

   
// create user interfaces with the declarative `view!` macro
    view! { cx,
        <div>
            <button on:click=clear>
"Clear"</button>
            <button on:click=decrement>
"-1"</button>
            <span>
"Value: " {value} "!"</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 /> })
}

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

  • VDOM 与细粒度: Yew 建立在虚拟 DOM (VDOM) 模型之上:状态更改导致组件重新渲染,生成新的虚拟 DOM 树。Yew 将此与之前的 VDOM 进行比较,并将这些补丁应用于实际的 DOM。只要状态发生变化,组件函数就会重新运行。乐浦采取了完全不同的方法。组件运行一次,创建(并返回)实际 DOM 节点并设置反应系统来更新这些 DOM 节点。
  • 性能:这对性能有巨大影响:乐浦在创建和更新 UI 方面比 Yew 快得多。(Dioxus 最近发布的 0.3 版本在性能方面取得了巨大进步,现在大致与 Leptos 持平。)
  • 心智模型:采用细粒度的反应性也往往会简化心智模型。没有令人惊讶的组件重新渲染,因为没有重新渲染。您可以在组件函数体内调用函数、创建超时等,因为它们不会重新运行。您无需考虑效果的手动依赖性跟踪;细粒度的反应性自动跟踪依赖关系。

这和Sycamore有什么不同?
从概念上讲,这两个框架非常相似:因为两者都是建立在细粒度反应性的基础上,所以大多数应用程序最终在两者之间看起来非常相似,Sycamore 或 Leptos 应用程序看起来都很像 SolidJS 应用程序,就像Yew 或 Dioxus 看起来很像 React。
有一些实际差异会产生显着差异:

  • 模板化: Leptos 的宏使用类似 JSX 的模板格式(基于syn-rsx构建)view。Sycamore 提供了自己的模板 DSL 或构建器语法的选择。
  • 服务器集成:乐浦提供了鼓励 HTML 流的原语,并允许轻松的异步集成和 RPC 调用,即使没有启用 WASM,也可以轻松选择前端和后端代码之间的集成,而不会将您推向任何特定的元框架模式。
  • 读写隔离: Leptos 与 Solid 一样,鼓励信号 getter 和 setter 之间的读写隔离,因此您最终可以使用元组访问信号,例如let (count, set_count) = create_signal(cx, 0); (如果您愿意或者它对您的 API 更方便,您可以使用create_rw_signal给定一个统一的读/写信号。)
  • 信号就是函数:在乐浦中,您可以调用信号来访问它,而不是调用特定的方法(因此,count()而不是count.get())这创建了一个更一致的心理模型:访问反应值始终是调用函数的问题。
  • 信号和作用域是 "static":Leptos和Sycamore都通过让信号变成Copy来缓解在闭包中移动信号的痛苦(特别是事件监听器),以避免{ let count = count.clone(); move |_| ...},这在Rust UI代码中是非常熟悉的。Sycamore通过使用凹凸分配将信号的寿命与作用域联系在一起来做到这一点:由于引用是Copy,&'a Signal<T>可以被移到一个闭包中。Leptos通过使用竞技场分配和传递索引来做到这一点:像ReadSignal<T>、WriteSignal<T>和Memo<T>这样的类型实际上是对进入竞技场的索引的包装。这意味着作用域和信号在Leptos中都是Copy和 "静态 "的,这意味着它们可以很容易地被移到闭包中而不增加生命周期的复杂性。