ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Leptos 同构计数应用实战:从浏览器调用服务端函数(counter_isomorphic 示例全解)

Leptos 同构计数应用实战:从浏览器调用服务端函数(counter_isomorphic 示例全解) Leptos 同构计数应用实战从浏览器调用服务端函数counter_isomorphic 示例全解【免费下载链接】leptosBuild fast web applications with Rust.项目地址: https://gitcode.com/GitHub_Trending/le/leptos本篇技术指南围绕 Leptos 仓库中的 counter_isomorphic 示例 展开系统讲解如何在 Leptos 中实现同构Isomorphic开发让同一套#[server]函数同时以服务端实现与浏览器端调用两种身份存在浏览器触发、服务端执行、结果回传并借此构建单用户、表单驱动与多用户实时三类计数器。读完本文你将掌握cargo leptos watch的启动方式、ssr/hydrate双 feature 构建、ActionResource的失效刷新模式以及基于 Server-Sent EventsSSE的多用户实时更新方案。示例简介什么是同构函数官方 README 对该示例的定位一句话即可概括This example demonstrates how to use a function isomorphically, to run a server side function from the browser and receive a result.即在浏览器中调用一个服务端函数并拿到返回值。这里的同构不是指在客户端与服务端各写一份实现而是同一个函数体经过编译期 feature 分支在服务端渲染SSR时以真实逻辑执行、在浏览器端hydrate时以fetch请求的形式被调用两边共用同一套函数签名与类型定义。在 counters.rs 中可以看到这种一份定义、双端可用的典型写法#[server] #[cfg_attr(feature ssr, instrument)] pub async fn get_server_count() - Resulti32, ServerFnError { use ssr_imports::*; Ok(COUNT.load(Ordering::Relaxed)) }#[server]宏由 leptos_macro 提供会自动生成两套代码在ssrfeature 下编译为真实的异步实现在客户端编译时则生成一个封装了 HTTP 调用URL 即该函数的 API 端点的桩函数调用者完全无感知。返回值统一使用ResultT, ServerFnError包装错误这也是 README 所说的从浏览器运行服务端函数并接收结果的底层机制。快速开始与运行环境示例 README 给出的快速启动命令只有一个cargo leptos watch它需要 cargo-leptos其中明确了完整的前置条件与操作步骤安装 Rust 与 Nightly 工具链并为当前工具链添加 WASM 目标rustup toolchain install nightly rustup target add wasm32-unknown-unknown安装 Cargo Make可选但推荐用于 CI 与一键启停cargo install --force cargo-make进入示例目录后使用cargo make ci完成环境准备与测试用cargo make start启动用cargo make stop结束进程。Makefile.toml 展示了该示例如何接入 cargo-make 体系通过extend字段继承 examples/cargo-make/main.toml 与 examples/cargo-make/cargo-leptos.toml其中start-client任务本质上执行的仍是cargo leptos watch。默认情况下客户端页面会运行在http://127.0.0.1:3000SSR 场景或http://127.0.0.1:8080CSR 场景具体以控制台输出为准。项目结构与双 feature 构建配置counter_isomorphic是典型的 Leptos SSR hydrate 项目目录结构如下examples/counter_isomorphic/ ├── Cargo.toml # features 与 cargo-leptos 构建元数据 ├── Makefile.toml # cargo-make 任务入口 ├── public/ # 静态资源favicon.ico └── src/ ├── main.rs # Actix Web 服务端入口SSR 侧 ├── lib.rs # hydrate 入口客户端侧 └── counters.rs # 核心业务server functions 三个计数器组件lib.rs 声明了crate-type [cdylib, rlib]见 Cargo.toml这是 Leptos 同构应用的标准配置rlib供服务端链接cdylib供wasm-bindgen生成 WASM 产物。客户端入口通过#[wasm_bindgen]暴露hydrate()#[cfg(feature hydrate)] #[wasm_bindgen::prelude::wasm_bindgen] pub fn hydrate() { use crate::counters::Counters; _ console_log::init_with_level(log::Level::Debug); console_error_panic_hook::set_once(); leptos::mount::hydrate_body(Counters); }服务端入口 main.rs 使用 Actix Web 构建 HTTP 服务核心链路包括get_configuration(None)读取构建配置 →generate_route_list(Counters)生成路由表 →leptos_routes(...)注册 Leptos 路由 →Files::new(/, site_root)托管静态资源并在head中注入AutoReload开发热重载与HydrationScripts水合脚本。对应的 Cargo.toml 定义了三个关键 featurehydrate开启leptos/hydrate用于编译浏览器端 WASMssr开启leptos/ssr、leptos_actix、actix-web、actix-files与tracing用于编译服务端bin-features [ssr]与lib-features [hydrate]告知 cargo-leptos 分别用哪组 feature 编译可执行文件与库从而实现一份代码、双端构建。[package.metadata.leptos]段还给出了站点根目录site-root target/site、产物目录site-pkg-dir pkg、静态资源目录assets-dir public、监听地址site-addr 127.0.0.1:3000与热重载端口reload-port 3001等关键参数。需要特别注意的是注释中的警告当不使用 cargo-leptos 运行时site-root必须改为.否则计数器功能无法正常工作。服务端共享状态跨连接的数据来源三个计数器的数据都存储在同一份服务端变量中这是理解整个示例的起点。counters.rs 中通过ssr_imports模块维护了进程级共享状态#[cfg(feature ssr)] pub mod ssr_imports { pub use broadcaster::BroadcastChannel; pub use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::LazyLock; pub static COUNT: AtomicI32 AtomicI32::new(0); pub static COUNT_CHANNEL: LazyLockBroadcastChanneli32 LazyLock::new(BroadcastChannel::i32::new); }COUNTAtomicI32保存计数器的绝对值对所有连接共享——这正是 README 中The value is shared across connections的出处也是打开另一个浏览器标签页即可验证的原因COUNT_CHANNELBroadcastChanneli32来自broadcastercrate负责把每一次变更广播给所有 SSE 订阅者供多用户计数器使用。三个 server function 全部读写这份共享状态get_server_count()读取当前值adjust_server_count(delta: i32, msg: String)累加/累减并广播新值同时println!(message {:?}, msg)打印客户端传入的消息clear_server_count()清零并广播。值得留意的是adjust_server_count的入参设计delta: i32与msg: String这种具名参数正好为后面的表单提交模式提供了数据载体。三种计数器模式由浅入深的同构调用范式页面通过 Leptos Router 在三个路由间切换见 counters.rs 的Counters组件Simple、formForm-Based与multiMulti-User。模式一Simple Counter —— Action Resource 的失效刷新这是典型的单用户 CRUD 模式源码注释明确写到 This is the typical pattern for a CRUD app。其核心思路是用Action提交写操作用Resource依赖写操作的版本号自动重取读接口let dec Action::new(|_: ()| adjust_server_count(-1, decing.into())); let inc Action::new(|_: ()| adjust_server_count(1, incing.into())); let clear Action::new(|_: ()| clear_server_count()); let counter Resource::new( move || { ( dec.version().get(), inc.version().get(), clear.version().get(), ) }, |_| get_server_count(), );关键机制在于version()Action每次成功 dispatch 都会递增内部版本号见 reactive_graph/src/actions/action.rs 的version()实现。当三个version()组成的元组任一发生变化时Resource的 source 失效并重新执行get_server_count()把服务端最新值拉回客户端。视图层再配合Suspense包裹读取结果spanValue: Suspense{counter} !/Suspense/spanErrorBoundary负责捕获服务端函数返回的错误并格式化展示保证调用失败时 UI 不崩溃。按钮通过on:click触发clear.dispatch(())、dec.dispatch(())、inc.dispatch(())完成操作。模式二Form Counter —— ServerAction ActionForm 的表单驱动该模式与 Simple Counter 行为一致但使用 HTML 表单提交动作README 与页面文案原话为 When progressively enhanced, it should behave identically to the Simple Counter。它的核心是#[server]宏自动生成的结构体——即函数名的 PascalCase 版本let adjust ServerAction::AdjustServerCount::new(); let clear ServerAction::ClearServerCount::new();ServerAction定义于 leptos_server/src/action.rs它封装了服务端函数并可直接与ActionForm绑定。注释中有一句点睛之笔calling a server function is the same as POSTing to its API URL因此完全可以用原生表单代替 JS 点击事件ActionForm actionadjust input typehidden namedelta value-1/ input typehidden namemsg valueform value down/ input typesubmit value-1/ /ActionForm这里input的name与#[server]函数参数一一对应delta、msg表单提交即等价于带具名参数的 POST 请求。失效刷新逻辑与模式一相同Resource依赖adjust.version()与clear.version()任何一次表单提交都会触发重新获取计数。由于整个交互走标准 HTML 表单该模式天然具备渐进增强progressive enhancement特性。模式三Multi-User Counter —— SSE 实时广播第三种模式解决的是多用户并发问题当一个用户修改计数时其他所有在线页面都要实时刷新。实现方式在 counters.rs 中明确标注It relies on a stream of server-sent events (SSE) for the counters value并注明这是live chat、collaborative editing 等实时应用的原语模式。服务端由 main.rs 的/api/events路由提供 SSE 流#[get(/api/events)] async fn counter_events() - impl Responder { let stream futures::stream::once(async { crate::counters::get_server_count().await.unwrap_or(0) }) .chain(COUNT_CHANNEL.clone()) .map(|value| { Ok(web::Bytes::from(format!( event: message\ndata: {value}\n\n ))) as Resultweb::Bytes }); HttpResponse::Ok() .insert_header((Content-Type, text/event-stream)) .streaming(stream) }流的第一帧是当前计数新订阅者立即获得初始值之后每次COUNT_CHANNEL广播新值都会推送给所有订阅者。客户端hydrate分支通过gloo_net::eventsource::futures::EventSource订阅该流let mut source SendWrapper::new( gloo_net::eventsource::futures::EventSource::new(/api/events) .expect(couldnt connect to SSE stream), ); let s ReadSignal::from_stream_unsync( source.subscribe(message).unwrap().map(|value| /* 解析 data */), ); on_cleanup(move || source.take().close());这里用SendWrapper包裹浏览器端非Send的流对象以满足编译约束用ReadSignal::from_stream_unsync把事件流转换为响应式信号on_cleanup在组件卸载时关闭连接。SSR 分支则退化为一个静态信号占位仅用于保证首屏渲染不报错——这也再次体现了同构代码通过#[cfg]在不同构建目标下各取所需的编排能力。从源码看同构调用的完整链路综合三个模式可以把浏览器 → 服务端函数 → 结果回传的完整链路梳理为编译期#[server]宏在客户端生成带 HTTP 请求逻辑的桩函数在服务端生成真实实现ssr与hydrate两个 feature 互斥地参与各自目标的编译Cargo.toml 的 feature 声明保证了这一点运行时客户端调用get_server_count()/adjust_server_count(...)时实际上向对应 URL 发起请求与表单 POST 等价状态同步写操作经Action/ServerAction提交读操作由Resource依赖version()自动触发实时场景则叠加 SSE 广播通道把服务端共享状态的变化推送给所有连接渲染Suspense处理异步值ErrorBoundary兜底错误二者共同保证水合hydration阶段的体验一致性。对Action/Resource细节感兴趣的读者可以在 reactive_graph/src/actions/action.rs 中查看version()与ActionI, O的定义ServerAction的具体封装则在 leptos_server/src/action.rs。小结counter_isomorphic虽然体量不大却浓缩了 Leptos 服务端集成中最核心的三套范式Action Resource 的失效刷新单用户 CRUD 的通用骨架、ServerAction ActionForm 的渐进增强表单无 JS 亦可工作的提交路径以及SSE 实时广播多人在线协作的基础。三套范式共享同一份#[server]函数与同一份服务端状态恰好完整诠释了同构二字写一次函数、双端复用、状态收敛于服务端。如需动手验证按 README 指引执行cargo leptos watch启动后先在第一个页面调整计数再打开第二个浏览器标签页观察值的变化随后切到 Multi-User 页面在任意标签页点击按钮即可看到 SSE 实时同步的效果。【免费下载链接】leptosBuild fast web applications with Rust.项目地址: https://gitcode.com/GitHub_Trending/le/leptos创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表