佔位標籤 (Suspense)
佔位標籤 (Suspense) 是一種在等待任務完成前暫停元件渲染的方式,同時顯示一個回退(佔位符)UI。
它可以用於從伺服器取得數據,等待代理完成任務,或執行其他後台非同步任務。
在佔位標籤出現之前,資料擷取通常發生在元件渲染之後(渲染時取得)或之前(取得後渲染)。
邊渲染,邊下載
佔位標籤 (Suspense) 提供了一種新的方法,允許元件在渲染過程中啟動資料請求。當元件啟動資料請求時,渲染過程將被暫停,並顯示一個回退 UI,直到請求完成。
建議使用鉤子 (Hook) 來使用佔位標籤。
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
鉤子將在載入使用者資訊時暫停元件渲染,並在載入 user
之前顯示 Loading...
佔位符。
要定義一個暫停元件渲染的鉤子,它需要傳回一個 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() {
// 當使用者載入完成時,我們將其作為 Ok(user) 傳回。
Some(m) => Ok(m),
None => {
// 當使用者仍在載入時,我們建立一個 `Suspension`
// 並在資料載入完成時呼叫 `SuspensionHandle::resume`,
// 元件將自動重新渲染。
let (s, handle) = Suspension::new();
on_load_user_complete(move || {handle.resume();});
Err(s)
},
}
}
關於實作暫停鉤子 (Hook) 的注意事項
Suspension::new
傳回 2 個值:暫停上下文本身和一個暫停句柄。後者負責在何時重新渲染暫停的元件,它提供了 2 種可互換的方法:
- 呼叫其
resume
方法。 - 丟棄句柄。
危險
暫停句柄必須儲存直到更新元件的時候,也就是使用新接收的資料;否則,暫停的元件將進入無限重新渲染循環,從而影響效能。
在上面的範例中,暫停句柄會透過移至閉包中並傳遞給 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!() // 略
}
fn on_load_user_complete<F: FnOnce()>(_fn: F) {
todo!() // 略
}
#[hook]
fn use_user() -> SuspensionResult<User> {
match load_user() {
// 如果用戶已加載,則將其作為 Ok(user) 返回。
Some(m) => Ok(m),
None => {
// 當使用者仍在載入時,我們建立一個 `Suspension`
// 並在資料載入完成時呼叫 `SuspensionHandle::resume`,
// 元件將自動重新渲染。
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>
}
}
在結構體組件中使用佔位標籤
直接暫停結構體組件是不可能的。然而,您可以使用函數元件作為高階元件來實現基於佔位標籤的資料取得。
Yew 倉庫中的佔位標籤範例示範如何使用這個元件。