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

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

相关推荐
耶啵奶膘13 分钟前
uniapp-是否删除
linux·前端·uni-app
2401_850410831 小时前
文件系统和日志管理
linux·运维·服务器
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
一只哒布刘3 小时前
NFS服务器
运维·服务器
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
lihuhelihu5 小时前
第3章 CentOS系统管理
linux·运维·服务器·计算机网络·ubuntu·centos·云计算