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

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

相关推荐
2501_915106323 小时前
移动端网页调试实战,iOS WebKit Debug Proxy 的应用与替代方案
android·前端·ios·小程序·uni-app·iphone·webkit
柯南二号4 小时前
【大前端】React Native 调用 Android、iOS 原生能力封装
android·前端·react native
睡美人的小仙女1275 小时前
在 Vue 前端(Vue2/Vue3 通用)载入 JSON 格式的动图
前端·javascript·vue.js
yuanyxh5 小时前
React Native 初体验
前端·react native·react.js
程序视点5 小时前
2025最佳图片无损放大工具推荐:realesrgan-gui评测与下载指南
前端·后端
KFCcrazy46 小时前
嵌入式学习日记(36)TCP并发服务器构建——epoll
服务器·学习·tcp/ip
程序视点7 小时前
2023最新HitPaw免注册版下载:一键去除图片视频水印的终极教程
前端
qq_297075677 小时前
网络安全测试(一)Kali Linux
linux·运维·服务器
四时久成8 小时前
服务器加密算法
服务器
小只笨笨狗~8 小时前
el-dialog宽度根据内容撑开
前端·vue.js·elementui