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>
僅作為依賴項解析和補丁的另一個邊界。
相關