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,因此引入了async
和await
。玩法和Javascript中的async
和await
类似,只能在async
代码块中使用await
。wasm_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
引用服务器端生成的数据,可以实现服务器端和客户端对同一段数据的刷新。在服务器端生成数据是为了提升页面的加载速度;在客户端获取数据是为了灵活的处理手段。这两者的结合,无疑让我们的应用会有更好的表现。
今天就写到这里,欢迎大家留言交流。