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

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

相关推荐
明 庭18 分钟前
通过 Docker 部署 pSQL 服务器的教程
服务器·docker·容器
弄不死的强仔22 分钟前
可被electron等调用的Qt截图-录屏工具【源码开放】
前端·javascript·qt·electron·贴图·qt5
霸王蟹40 分钟前
el-table组件样式如何二次修改?
前端·javascript·vue.js·笔记·学习·前端框架
美味小鱼1 小时前
Rust 所有权特性详解
开发语言·后端·rust
star010-1 小时前
一文学会HTML编程之视频+图文详解详析
前端·网络·网络安全·html·html5
star010-2 小时前
【视频+图文详解】HTML基础3-html常用标签
前端·css·网络安全·html·html5·学习方法
身在江湖的郭大侠2 小时前
Linux内核
linux·服务器
破-风4 小时前
linux的用法
linux·运维·服务器
Hello.Reader6 小时前
深入浅出 Rust 的强大 match 表达式
开发语言·后端·rust
无限大.7 小时前
前端知识速记:节流与防抖
前端