[2023.09.27]: Yew SSR开发中的服务器端与客户端共同维护同一状态的实践

SSR(Server-Side Rendering)具有许多优势,其中之一就是能够在服务器端生成页面,从而提高整个页面的加载速度。在Yew SSR开发模式中,我们可以使用use_prepared_state宏在服务器端获取数据并生成Html返回到客户端。官方提供了相应的例子供参考。然而,如果在客户端修改并提交了该数据,我们需要如何刷新之前在服务器端生成的数据呢?官方未提供相应例子,经过多次实验后,以下代码可以实现该功能。

rust 复制代码
#[function_component]
fn Content() -> HtmlResult {
    let datas: Vec<Note> =
        use_prepared_state!(async move |_| -> Vec<Note> { fetch_notes().await }, ())?
            .unwrap()
            .to_vec();
    let data_state = {
        let datas = datas.clone();
        use_state(move || datas)
    };
    let data = (*data_state).clone().into_iter().collect::<Vec<TableRow>>();

    ...
    
    let reload = use_callback(
        move |_, data_state1| {
            let data_state1 = data_state1.clone();
            spawn_local(async move {
                if let Ok(resp) = Request::get("/api/notes").send().await {
                    if let Ok(datas1) = resp.json::<Vec<Note>>().await {
                        data_state1.set(datas1);
                    }
                }
            });
        },
        (data_state),
    );
    
    let on_delete_row = {
        let reload = reload.clone();
        move |row: TableRow| {
            let reload = reload.clone();
            spawn_local(async move {
                if let Ok(req) = Request::delete(&format!("/api/notes/{}", row.id)).build() {
                    let _ = req.send().await;
                    reload.emit(());
                } else {
                    log!("delete failed");
                }
            });
        }
    };

    
    Ok(html! {
        <div class="column">
            ...
            <TableComponent data={data} on_delete={on_delete_row}/>
            ...
        </div>
    })
}

代码解析

上面的代码是一个Yew的函数式组件。在Yew SSR开发模式中,官方推荐使用函数式组件来进行开发。
datas是在服务器端生成的数据。紧接着我们用use_state来引用这个数据。因此,后面的处理就围绕data_state来进行。

rust 复制代码
let data_state = {
        let datas = datas.clone();
        use_state(move || datas)
    };

data_state中获取数据,并将其直接传入组件的属性,从而使数据渲染到界面上。

rust 复制代码
    let data = (*data_state).clone().into_iter().collect::<Vec<TableRow>>();
    ...
    Ok(html! {
        <div class="column">
            ...
            <TableComponent data={data} on_delete={on_delete_row}/>
            ...
        </div>
    })

当数据通过on_delete_row被删除后,需要重新获取数据,然后刷新该数据在界面上的显示,注意这个过程是在客户端发生的。当然,我们可以强行刷新页面,利用服务器端来从新生成数据,即datas。但是这种方式体验感不好,感觉回到了20年前的Asp.net的页面开发。

rust 复制代码
    let on_delete_row = {
        let reload = reload.clone();
        move |row: TableRow| {
            let reload = reload.clone();
            spawn_local(async move {
                if let Ok(req) = Request::delete(&format!("/api/notes/{}", row.id)).build() {
                    let _ = req.send().await;
                    reload.emit(());
                } else {
                    log!("delete failed");
                }
            });
        }
    };

在上面的代码中reload的触发就是为了重新加载并刷新页面。在这里使用了Future,因此引入了asyncawait。玩法和Javascript中的asyncawait类似,只能在async代码块中使用awaitwasm_bindgen_futures::spawn_local函数是一种在WebAssembly中启动异步任务的方式,因此访问后端api的地方都会用到spawn_local函数。

最后,在reload回调函数中,通过data_state1.set(datas1),将后端获取到的数据更新到界面上。

rust 复制代码
    let reload = use_callback(
        move |_, data_state1| {
            let data_state1 = data_state1.clone();
            spawn_local(async move {
                if let Ok(resp) = Request::get("/api/notes").send().await {
                    if let Ok(datas1) = resp.json::<Vec<Note>>().await {
                        data_state1.set(datas1);
                    }
                }
            });
        },
        (data_state),
    );

总结

通过use_state引用服务器端生成的数据,可以实现服务器端和客户端对同一段数据的刷新。在服务器端生成数据是为了提升页面的加载速度;在客户端获取数据是为了灵活的处理手段。这两者的结合,无疑让我们的应用会有更好的表现。

今天就写到这里,欢迎大家留言交流。

相关推荐
XiaoFan01217 小时前
免密批量抓取日志并集中输出
java·linux·服务器
souyuanzhanvip17 小时前
ServerBox v1.0.1316 跨平台 Linux 服务器管理工具
linux·运维·服务器
木斯佳18 小时前
前端八股文面经大全:26届秋招滴滴校招前端一面面经-事件循环题解析
前端·状态模式
光影少年18 小时前
react状态管理都有哪些及优缺点和应用场景
前端·react.js·前端框架
roman_日积跬步-终至千里18 小时前
【LangGraph4j】LangGraph4j 核心概念与图编排原理
java·服务器·数据库
野犬寒鸦19 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
saber_andlibert19 小时前
TCMalloc底层实现
java·前端·网络
逍遥德19 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~19 小时前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions20 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生