news 2026/9/13 3:03:02

Leptos Counter 示例全解析:用 Rust 与 WASM 构建客户端渲染计数器应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Leptos Counter 示例全解析:用 Rust 与 WASM 构建客户端渲染计数器应用

Leptos Counter 示例全解析:用 Rust 与 WASM 构建客户端渲染计数器应用

【免费下载链接】leptosBuild fast web applications with Rust.项目地址: https://gitcode.com/GitHub_Trending/le/leptos

本文以 leptos 仓库中的 examples/counter 为例,完整讲解如何在客户端渲染(CSR)模式下,用 Rust 编写组件、使用响应式信号(signal)与事件处理,并通过 Trunk 构建为 WebAssembly 应用运行在浏览器中。读完本文,你将掌握 Leptos 最核心的#[component]组件写法、signal读写模式、view!宏语法、浏览器端挂载方式,以及如何在真实浏览器中对其做 DOM 级自动化测试。

示例概览:一个最简单的 Leptos 应用长什么样

examples/counter是 leptos 仓库中最基础的入门示例,它在纯客户端渲染的 WebAssembly 应用中实现了一个计数器:包含 "Clear"、"−1"、"+1" 三个按钮和一个显示Value: N!的文本节点。整个示例由三部分构成:

文件作用
examples/counter/src/lib.rs定义可复用的SimpleCounter组件(组件库部分)
examples/counter/src/main.rs应用入口,初始化日志并把组件挂载到页面
examples/counter/index.htmlTrunk 构建的 HTML 模板,声明 wasm 与图标资源
examples/counter/tests/web.rs基于wasm-bindgen-test的浏览器内测试
examples/counter/Cargo.toml依赖与 release 优化配置

从源码结构看,这是 leptos 官方推荐的"库 + 入口"分离结构:把组件放在lib.rs里导出,main.rs只负责挂载,这样组件可以在测试中直接复用(tests/web.rs 中正是通过use counter::*引入组件进行测试的)。

环境准备与快速启动

示例 README 明确给出了最简启动命令:

trunk serve --open

该命令会启动 Trunk 开发服务器并自动在浏览器中打开页面。由于这是一个 CSR 应用,trunk负责把 Rust 源码编译成wasm32-unknown-unknown目标并作为前端资源提供。完整的运行前提如下:

  1. 安装 Rust 工具链并切换到 nightly(示例目录下的 rust-toolchain.toml 已声明targets = ["wasm32-unknown-unknown"],确保该 target 已通过rustup target add wasm32-unknown-unknown安装);
  2. 安装 Trunk:cargo install trunk
  3. 进入示例目录执行trunk serve --open

更详细的通用示例运行说明见 examples/README.md。此外每个示例的 Makefile.toml 都extend了 examples/cargo-make/main.toml 等任务文件,因此也可以用cargo-make执行cargo make cicargo make startcargo make stop来搭建、运行和清理示例进程,这是 CI 中使用的可选方式。

index.html:Trunk 如何知道要编译什么

<!doctype html> <html> <head> <link>use leptos::prelude::*; /// A simple counter component. /// /// You can use doc comments like this to document your component. #[component] pub fn SimpleCounter( /// The starting value for the counter initial_value: i32, /// The change that should be applied each time the button is clicked. step: i32, ) -> impl IntoView { let (value, set_value) = signal(initial_value); view! { <div> <button on:click=move |_| set_value.set(0)>"Clear"</button> <button on:click=move |_| *set_value.write() -= step>"-1"</button> <span>"Value: " {value} "!"</span> <button on:click=move |_| set_value.update(|value| *value += step)>"+1"</button> </div> } }

要点拆解:

  • 组件即函数#[component]把普通函数转换为 Leptos 组件,函数的普通参数initial_value: i32step: i32就是组件的属性(props),在使用处通过<SimpleCounter initial_value=0 step=1/>传入;
  • 返回impl IntoView:组件返回值是实现了IntoViewtrait 的类型,view!宏生成的HtmlElement满足这一约束;
  • doc 注释即组件文档:源码注释说明你可以在组件上用文档注释记录用途,IDE 与文档生成工具都能直接受益;
  • signal(initial_value):创建一个由i32初始值驱动的响应式信号,返回读写元组(ReadSignal, WriteSignal)signal是 leptos 响应式系统的核心 API,其实现位于 reactive_graph/src/signal.rs,底层由reactive_graph提供信号读取/写入的同步追踪机制,UI 依赖的信号变化会自动触发相关视图更新。

三种信号写入方式

计数器示例在一个组件里演示了WriteSignal的三种典型用法,这也是实战中最常用的三种写入 API:

写法代码行为
set()set_value.set(0)直接整体覆盖为0,用于 "Clear" 按钮
write()*set_value.write() -= step返回&mut i32,原地修改值再写回,用于 "−1" 按钮
update()set_value.update(\|value\| *value += step)传入闭包对当前值做任意变换,用于 "+1" 按钮

视图模板:view!宏与事件绑定

view!是 leptos 的 JSX 风格模板宏,在src中通过leptos::prelude::*引入。以上组件为例:

view! { <div> <button on:click=move |_| set_value.set(0)>"Clear"</button> <span>"Value: " {value} "!"</span> ... </div> }
  • 文本节点"Clear"这类字符串字面量直接渲染为文本;{value}表示把ReadSignal<i32>作为响应式表达式插入,当信号值变化时该文本节点会自动更新——这就是"响应式"在视图层的体现;
  • 事件绑定on:click=move |_| ...是标准事件绑定语法,on:后跟 DOM 事件名,值为闭包。由于闭包要捕获set_value,用move关键字转移所有权;_是事件参数(这里不需要MouseEvent本身);
  • 表达式插值{value}之外,"Value: ""!"是静态文本,三者共同组成一个动态文本节点。

入口与挂载:main.rs 的职责

use counter::SimpleCounter; use leptos::prelude::*; pub fn main() { _ = console_log::init_with_level(log::Level::Debug); console_error_panic_hook::set_once(); mount_to_body(|| { view! { <SimpleCounter initial_value=0 step=1/> } }) }

入口做了三件事:

  1. 初始化日志console_log::init_with_level(log::Level::Debug)log输出转发到浏览器 console,便于调试;
  2. 安装 panic 钩子console_error_panic_hook::set_once()让 Rust panic 以可读形式显示在 console 中,否则 wasm 中的 panic 信息会很难定位;
  3. 挂载根组件mount_to_body(|| view! { <SimpleCounter initial_value=0 step=1/> })把组件渲染进<body>mount_to_body定义在 leptos/src/mount.rs(pub fn mount_to_body),同文件还提供mount_to(挂载到指定元素并返回UnmountHandle,测试中用来挂到测试容器)与mount_to_renderer等挂载 API。

main.rs里的main()不是fn main() -> Result<(), Box<dyn Error>>这样的服务器入口,而是会被编译为 wasm 并作为模块入口导出的函数,配合wasm-bindgen由 Trunk 处理。

依赖与构建优化:Cargo.toml 解读

[package] name = "counter" version = "0.1.0" edition = "2021" [profile.release] opt-level = 'z' codegen-units = 1 lto = true [dependencies] leptos = { path = "../../leptos", features = ["csr"] } console_log = "1.0" log = "0.4.22" console_error_panic_hook = "0.1.7" gloo-timers = { version = "0.3.0", features = ["futures"] } [dev-dependencies] wasm-bindgen = "0.2.93" wasm-bindgen-test = "0.3.42" web-sys = "0.3.70"
  • leptos = { path = "../../leptos", features = ["csr"] }:以仓库内路径依赖当前主线 leptos,并开启csrfeature——它决定了本应用按纯客户端渲染模式编译,不包含 SSR/hydration 相关代码路径;
  • opt-level = 'z'codegen-units = 1lto = true:针对 wasm 体积与性能的发布优化:z以体积优先优化、单 codegen unit 配合全量 LTO 允许跨 crate 内联,显著减小最终 wasm 体积并提升运行性能;
  • gloo-timers:提供set_timeout等定时器能力(带 futures feature),示例依赖中保留它便于演示异步能力;
  • dev-dependencieswasm-bindgenwasm-bindgen-testweb-sys用于在浏览器里做 DOM 测试。

浏览器内测试:wasm-bindgen-test 实战

tests/web.rs展示了如何在真实浏览器里验证组件行为。测试使用wasm_bindgen_test_configure!(run_in_browser),让测试跑在浏览器环境(而不是 node),并通过wasm-bindgen-test提供的#[wasm_bindgen_test]宏声明异步测试。

clear 测试的核心流程:

  1. 创建测试容器<section>并挂到document.body
  2. mount_to(test_wrapper, || view! { <SimpleCounter initial_value=10 step=1/> })渲染初始值为 10 的计数器,注意这里用的是 mount.rs 中的mount_to,可挂载到任意元素;
  3. query_selector("button")拿到 "Clear" 按钮并click()
  4. 调用tick().await让响应式系统完成一轮微任务刷新(注释明确说明:响应式建立在异步系统之上,DOM 不会同步更新,必须让 effect 跑完);
  5. outer_html()/inner_html()与"用signal(0)重新渲染的期望视图"对比,验证点击后值归零。

inc 测试则演示了更朴素的 DOM 操作方式:通过first_child()/next_sibling()遍历按钮与文本节点,连点 "+1" 两次后断言text.text_content() == "Value: 2!",连点 "−1" 四次后断言"Value: -2!",再点 "Clear" 断言"Value: 0!"。其中还展示了一个实用的测试技巧:用signal(n)构造期望文本、再.into_view().build().outer_html()生成对比 HTML——注释指出into_view()在这里只是"使用常规 DOM 渲染器"的便捷写法,视图是惰性的,build()才会真正创建 DOM 元素并返回ElementState

运行与验证

# 进入示例目录 cd examples/counter # 启动 Trunk 开发服务器并自动打开浏览器 trunk serve --open # 如需运行浏览器内测试(以 wasm-bindgen-test 方式) # 需要先为 wasm32-unknown-unknown 目标安装测试 runner(如 wasm-bindgen-test-runner)

打开页面后,你将看到四个元素:"Clear"、"−1"、Value: 0!、"+1"。点击 "+1" 文本实时变为Value: 1!;连续点击 "−1" 变为负数;点击 "Clear" 立即归零。整个过程中没有页面刷新——所有状态变更都由 leptos 响应式信号驱动,最终以最小 DOM 更新的方式同步到视图。

小结

examples/counter虽然只有几十行代码,却完整覆盖了 leptos 开发的核心链路:#[component]组件定义与属性传参、signal响应式状态、view!模板与事件绑定、CSR 模式下的入口挂载,以及基于wasm-bindgen-test的真实浏览器 DOM 测试。以此为起点,你可以沿着仓库中的 examples/README.md 继续探索其他示例(如counter_isomorphiccounter_without_macrostodo_app_sqlite等),把同样的组件与响应式模式扩展到 SSR、路由、服务端函数等更复杂场景。

【免费下载链接】leptosBuild fast web applications with Rust.项目地址: https://gitcode.com/GitHub_Trending/le/leptos

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 3:02:50

软考软件设计师下午题六:Java设计模式填空套路与高分实务

想拿下软考软件设计师的下午题&#xff0c;第六题Java设计模式基本上是一道绕不开的“标准题”。我自己备考那会儿&#xff0c;很多人前面的选择题刷得飞起&#xff0c;一到下午题就卡壳&#xff0c;尤其是最后一题&#xff0c;代码填空看着眼熟&#xff0c;但真要动手填&#…

作者头像 李华
网站建设 2026/9/13 3:01:36

基于 Kustomize Components 的多集群差异化配置优雅编排

基于 Kustomize Components 的多集群差异化配置优雅编排在全面推行声明式 GitOps&#xff08;ArgoCD Kustomize&#xff09;的过程中&#xff0c;随着业务不断扩展到多个地理数据中心&#xff08;如华东主力集群、华北容灾集群、欧洲跨境合规集群&#xff09;以及多种异构计算…

作者头像 李华
网站建设 2026/9/13 3:01:32

基于反事实因果推理的根因验证与排除

基于反事实因果推理的根因验证与排除在大型分布式系统的智能故障诊断中&#xff0c;传统的根因分析&#xff08;RCA&#xff09;算法在给出推荐结果后&#xff0c;常常面临一个令人头疼的致命弱点&#xff1a;“高置信度的伪阳性&#xff08;High Confidence False Positives&a…

作者头像 李华
网站建设 2026/9/13 3:01:19

Ubuntu 22.04 + VS Code 搭建 PySide6 开发环境实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 2:59:37

国控断面坐标数据清洗到空间质检:从经纬度到GIS制图的完整路径

简介&#xff1a;山东省地表水水质国控断面坐标数据提供了全省56个国控断面的核心位置信息&#xff0c;面向环境监测、水利规划与GIS分析人员。数据集包含所属省、市、流域、河流、断面名称及经纬度等关键属性&#xff0c;可用于绘制断面空间分布图、追踪水质变化趋势&#xff…

作者头像 李华
网站建设 2026/9/13 2:58:39

情感识别模型ONNX部署实战:CUDA多版本与GPU优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华