SSR(Server-Side Rendering)具有许多优势,其中之一就是能够在服务器端生成页面,从而提高整个页面的加载速度。在Yew SSR开发模式中,我们可以使用use_prepared_state宏在服务器端获取数据并生成Html返回到客户端。官方提供了相应的例子供参考。然而,如果在客户端修改并提交了该数据,我们需要如何刷新之前在服务器端生成的数据呢?官方未提供相应例子,经过多次实验后,以下代码可以实现该功能。
#[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
来进行。
let data_state = {let datas = datas.clone();use_state(move || datas)};
从data_state
中获取数据,并将其直接传入组件的属性,从而使数据渲染到界面上。
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的页面开发。
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,因此引入了async
和await
。玩法和Javascript中的async
和await
类似,只能在async
代码块中使用await
。wasm_bindgen_futures::spawn_local
函数是一种在WebAssembly中启动异步任务的方式,因此访问后端api的地方都会用到spawn_local
函数。
最后,在reload
回调函数中,通过data_state1.set(datas1)
,将后端获取到的数据更新到界面上。
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
引用服务器端生成的数据,可以实现服务器端和客户端对同一段数据的刷新。在服务器端生成数据是为了提升页面的加载速度;在客户端获取数据是为了灵活的处理手段。这两者的结合,无疑让我们的应用会有更好的表现。
今天就写到这里,欢迎大家留言交流。