Suspense
實驗性功能
<Suspense>是一個實驗性功能。它可能無法達到穩定狀態,並且在其達到穩定狀態之前,API可能會發生變化。
<Suspense>是一個內建元件,用於在元件樹中協調非同步依賴。在等待多個巢狀非同步依賴在元件樹中解決時,它可以渲染載入狀態。
非同步依賴
為了解釋<Suspense>試圖解決的問題以及它與這些非同步依賴的互動方式,讓我們想象一個如下所示的元件層次結構
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus> (component with async setup())
└─ <Content>
├─ <ActivityFeed> (async component)
└─ <Stats> (async component)在元件樹中,有多個巢狀元件,它們的渲染依賴於一些需要首先解決的非同步資源。在沒有<Suspense>的情況下,每個元件都需要處理自己的載入/錯誤和載入完成狀態。在最壞的情況下,我們可能在頁面上看到三個載入指示器,內容在不同時間顯示。
<Suspense>元件為我們提供了在等待這些巢狀非同步依賴解決時顯示頂層載入/錯誤狀態的能力。
<Suspense>可以等待兩種型別的非同步依賴
具有非同步
setup()鉤子的元件。這包括使用帶有頂級await表示式的<script setup>的元件。
async setup()
Composition API 元件的 setup() 鉤子可以是非同步的
js
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}如果使用 <script setup>,頂級 await 表示式的存在會自動使元件成為非同步依賴
vue
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>非同步元件
非同步元件預設是 "可掛起的"。這意味著如果它在父級鏈中有一個 <Suspense>,它將被視為該 <Suspense> 的非同步依賴。在這種情況下,載入狀態將由 <Suspense> 控制,並且將忽略元件自己的載入、錯誤、延遲和超時選項。
非同步元件可以透過在其選項中指定 suspensible: false 來選擇退出 Suspense 控制,並讓元件始終控制其自己的載入狀態。
載入狀態
<Suspense> 元件有兩個插槽:#default 和 #fallback。這兩個插槽都只允許有一個直接子節點。如果可能,將顯示預設插槽中的節點。如果不可以,將顯示回退插槽中的節點。
template
<Suspense>
<!-- component with nested async dependencies -->
<Dashboard />
<!-- loading state via #fallback slot -->
<template #fallback>
Loading...
</template>
</Suspense>在初次渲染時,<Suspense> 將在記憶體中渲染其預設插槽內容。如果在處理過程中遇到任何非同步依賴,它將進入一個 掛起 狀態。在掛起狀態下,將顯示回退內容。當所有遇到的非同步依賴都已解決,<Suspense> 進入一個 已解決 狀態,並顯示已解決的預設插槽內容。
如果在初次渲染過程中沒有遇到任何非同步依賴,<Suspense> 將直接進入已解決狀態。
一旦進入已解決狀態,<Suspense> 只有在 #default 插槽的根節點被替換時才會回到掛起狀態。巢狀在樹中的新非同步依賴 不會 導致 <Suspense> 回到掛起狀態。
當發生回退時,回退內容不會立即顯示。相反,<Suspense> 將在等待新內容及其非同步依賴解決的同時顯示之前的 #default 內容。此行為可以透過 timeout 屬性進行配置:<Suspense> 將在 timeout 指定的時間後切換到回退內容。如果 timeout 的值為 0,則當預設內容被替換時,將立即顯示回退內容。
事件
<Suspense> 元件發出 3 個事件:pending、resolve 和 fallback。當進入掛起狀態時,將觸發 pending 事件。當新內容在 default 插槽中完成解決時,將發出 resolve 事件。當顯示 fallback 插槽的內容時,將觸發 fallback 事件。
可以使用這些事件,例如,在載入新元件時在舊 DOM 前顯示載入指示器。
錯誤處理
<Suspense> 目前不透過元件本身提供錯誤處理 - 然而,您可以使用 errorCaptured 選項或 onErrorCaptured() 鉤子來捕獲和處理 <Suspense> 父元件中的非同步錯誤。
與其他元件結合
在組合使用<Transition>和<KeepAlive>元件時,通常需要使用<Suspense>。這些元件的巢狀順序對於它們正常工作至關重要。
此外,這些元件通常與Vue Router中的<RouterView>元件一起使用。
以下示例展示瞭如何巢狀這些元件,以便它們都按預期工作。對於更簡單的組合,您可以刪除不需要的元件。
template
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- main content -->
<component :is="Component"></component>
<!-- loading state -->
<template #fallback>
Loading...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>Vue Router內建了對使用動態匯入進行懶載入元件的支援。這些與非同步元件不同,目前它們不會觸發<Suspense>。然而,它們可以作為非同步元件的子元件,並且可以透過常規方式觸發<Suspense>。
巢狀Suspense
- 僅在3.3及以上版本支援
當我們有多個非同步元件(常見於巢狀或基於佈局的路由)時,如下所示:
template
<Suspense>
<component :is="DynamicAsyncOuter">
<component :is="DynamicAsyncInner" />
</component>
</Suspense><Suspense>建立了一個邊界,將解析樹中的所有非同步元件,正如預期的那樣。然而,當我們更改DynamicAsyncOuter時,<Suspense>正確等待它,但當更改DynamicAsyncInner時,巢狀的DynamicAsyncInner將渲染一個空節點,直到它被解析(而不是之前的節點或回退插槽)。
為了解決這個問題,我們可以使用巢狀的<Suspense>來處理巢狀元件的補丁,如下所示:
template
<Suspense>
<component :is="DynamicAsyncOuter">
<Suspense suspensible> <!-- this -->
<component :is="DynamicAsyncInner" />
</Suspense>
</component>
</Suspense>如果您沒有設定suspensible屬性,父級<Suspense>會將內部<Suspense>視為同步元件。這意味著它有自己的回退插槽,如果兩個Dynamic元件同時更改,在子<Suspense>載入其自己的依賴項樹時,可能會有空節點和多個補丁週期,這可能不是所希望的。當設定了該屬性後,所有非同步依賴項處理都由父級<Suspense>處理(包括髮出的事件),而內部<Suspense>僅作為依賴項解析和補丁的另一個邊界。
相關