Suspense
Suspense 是一种在等待任务完成时暂停组件渲染的方式,同时会显示一个后备(占位符)UI。
它可用于从服务器获取数据、等待代理完成任务或执行其他后台异步任务。
在有 suspense 之前,数据获取通常发生在组件渲染之后(Fetch-on-render)或之前(Fetch-then-render)。
渲染即获取
Suspense 启用了一种新方法,允许组件在渲染过程中发起数据请求。当组件发起数据请求时,渲染过程将暂停,并且将显示一个备用 UI,直到请求完成。
使用 suspense 的推荐方法是使用钩子。
use yew::prelude::*;
#[function_component(Content)]
fn content() -> HtmlResult {
let user = use_user()?;
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
}
#[function_component(App)]
fn app() -> Html {
let fallback = html! {<div>{"Loading..."}</div>};
html! {
<Suspense {fallback}>
<Content />
</Suspense>
}
}
在上面的示例中,use_user
钩子将在加载用户信息时暂停组件渲染,并且将显示一个 Loading...
占位符,直到加载 user
。
要定义一个暂停组件渲染的钩子,它需要返回一个 SuspensionResult<T>
。当需要暂停组件时,钩子应返回一个 Err(Suspension)
,用户应使用 ?
解开它,其中它将被转换为 Html
。
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
struct User {
name: String,
}
#[hook]
fn use_user() -> SuspensionResult<User> {
match load_user() {
// If a user is loaded, then we return it as Ok(user).
Some(m) => Ok(m),
None => {
// When user is still loading, then we create a `Suspension`
// and call `SuspensionHandle::resume` when data loading
// completes, the component will be re-rendered
// automatically.
let (s, handle) = Suspension::new();
on_load_user_complete(move || {handle.resume();});
Err(s)
},
}
}
有关实现暂停钩子的说明
Suspension::new
返回 2 个值:暂停上下文本身和一个暂停句柄。后者负责发出重新渲染已暂停组件的信号,它提供了 2 种可互换的方式来执行此操作
- 调用它的
resume
方法。 - 删除句柄。
danger
必须存储暂停句柄,直到更新组件(即使用新接收的数据)为止;否则,已暂停的组件将进入无限重新渲染循环,从而阻碍性能。在上面的示例中,通过将暂停句柄移入闭包并传递到 on_load_user_complete
中来保留暂停句柄。当加载假设的用户时,将调用闭包,从而调用 handle.resume()
并重新渲染与暂停上下文关联的组件。
完整示例
use yew::prelude::*;
use yew::suspense::{Suspension, SuspensionResult};
#[derive(Debug)]
struct User {
name: String,
}
fn load_user() -> Option<User> {
todo!() // implementation omitted.
}
fn on_load_user_complete<F: FnOnce()>(_fn: F) {
todo!() // implementation omitted.
}
#[hook]
fn use_user() -> SuspensionResult<User> {
match load_user() {
// If a user is loaded, then we return it as Ok(user).
Some(m) => Ok(m),
None => {
// When user is still loading, then we create a `Suspension`
// and call `SuspensionHandle::resume` when data loading
// completes, the component will be re-rendered
// automatically.
let (s, handle) = Suspension::new();
on_load_user_complete(move || {handle.resume();});
Err(s)
},
}
}
#[function_component(Content)]
fn content() -> HtmlResult {
let user = use_user()?;
Ok(html! {<div>{"Hello, "}{&user.name}</div>})
}
#[function_component(App)]
fn app() -> Html {
let fallback = html! {<div>{"Loading..."}</div>};
html! {
<Suspense {fallback}>
<Content />
</Suspense>
}
}
在结构组件中使用 Suspense
无法直接挂起结构组件。但是,你可以将函数组件用作高阶组件来实现基于悬念的数据获取。
Yew 存储库中的悬念示例演示了如何使用。