vue3中的內(nèi)置組件<Suspense>用法
官方文檔:
官方提示:
<Suspense>是一項實驗性功能。它不一定會最終成為穩(wěn)定功能,并且在穩(wěn)定之前相關 API 也可能會發(fā)生變化。<Suspense>是一個內(nèi)置組件,用來在組件樹中協(xié)調(diào)對異步依賴的處理。它讓我們可以在組件樹上層等待下層的多個嵌套異步依賴項解析完成,并可以在等待時渲染一個加載狀態(tài)。
異步依賴
要了解 <Suspense> 所解決的問題和它是如何與異步依賴進行交互的,需要了解什么是異步依賴。
<Suspense>主要用于包裹異步組件,這些組件可能需要進行異步數(shù)據(jù)獲取或其他耗時的操作:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus>(組件有異步的 setup())
└─ <Content>
├─ <ActivityFeed> (異步組件)
└─ <Stats>(異步組件)
在這個組件樹中有多個嵌套組件,要渲染出它們,首先得解析一些異步資源。
如果沒有 <Suspense>組件:
- 以vue2 項目為例:這個組件樹中的全部組件,都需要處理各自的加載、報錯和完成狀態(tài)。
- 在網(wǎng)絡不好或數(shù)據(jù)量過大的時候,可以看到頁面上有多個加載狀態(tài),它們在不同的時間加載完成,頁面顯示出內(nèi)容。
有了 <Suspense> 組件后:
- 可以在等待整個多層級組件樹中的各個異步依賴獲取結果時,在頂層展示出加載中或加載失敗的狀態(tài)。
<Suspense> 可以等待的異步依賴有兩種:
- 帶有異步
setup()鉤子的組件。這也包含了使用<script setup>時有頂層await表達式的組件。 - 異步組件。
async setup()
組合式 API 中組件的 setup() 鉤子可以是異步的:
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
<script setup>語法糖
在<script setup>語法糖中,開發(fā)者無法在前面添加async。
如果使用 <script setup>,那么頂層 await 表達式會自動讓該組件成為一個異步依賴:
<!-- Child.vue -->
<template>
<div class="child-box">
<h1>子組件</h1>
<h2>網(wǎng)易熱歌榜</h2>
</div>
</template>
<script setup lang="ts">
// 獲取網(wǎng)易云熱歌榜
let res = await fetch('https://api.uomg.com/api/rand.music?sort=熱歌榜&format=json')
console.log(res.json()) // 一個Promise
</script>
在父組件中直接引用Child.vue組件:
<template>
<div class="home-wrap">
<h1>主頁----父組件</h1>
<Child />
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
</script>
頁面沒有顯示Child.vue的內(nèi)容,并且在控制臺提示:
[Vue warn]: Component : setup function returned a promise, but no boundary was found in the parent component tree. A component with async setup() must be nested in a in order to be rendered.

setup函數(shù)返回了一個promise,但是在父組件樹中沒有找到<Suspense> 邊界。帶有async setup()的組件必須嵌套在<Suspense> 中才能呈現(xiàn)。
按照提示修改父組件:
<template>
<div class="home-wrap">
<h1>主頁----父組件</h1>
<Suspense>
<!-- 異步任務ok后 -->
<template v-slot:default>
<Child />
</template>
<!-- 異步任務未做完時 -->
<template v-slot:fallback>
<div>請求中...</div>
</template>
</Suspense>
</div>
</template>
<script setup lang="ts">
import { Suspense } from 'vue';
import Child from './Child.vue';
</script>
頁面成功展示Child.vue的內(nèi)容:

異步組件
異步組件默認就是suspensible: true的。這意味著如果組件關系鏈上有一個 <Suspense>,那么這個異步組件就會被當作這個 <Suspense> 的一個異步依賴。在這種情況下,加載狀態(tài)是由 <Suspense> 控制,而該組件自己的加載、報錯、延時和超時等選項都將被忽略。
異步組件也可以通過在選項中指定 suspensible: false 表明不用 Suspense 控制,并讓組件始終自己控制其加載狀態(tài)。
<Suspense>
props
interface SuspenseProps {
timeout?: string | number
suspensible?: boolean
}
參數(shù)說明:
timeout:可選參數(shù)。
- 類型:
string | number - 作用:指定一個超時時間。如果異步依賴在這個時間內(nèi)沒有解析完成,
<Suspense>將不再等待并顯示fallback內(nèi)容。
suspensible:可選參數(shù)。
- 類型:
boolean,默認值為false。 - 作用:當設置為
true時,組件樹中所有的異步依賴將由父級 Suspense 處理。這對于構建復雜的異步加載場景非常有用,可以將多個異步依賴的處理集中在一個更高層次的<Suspense>組件中。
插槽
<Suspense> 組件接受兩個插槽:#default 和 #fallback。兩個插槽都只允許一個直接子節(jié)點。
#default 插槽用于放置異步依賴的組件或內(nèi)容。
如果在渲染時遇到異步組件或具有 async setup() 的組件,<Suspense> 會等待所有異步依賴項解析完成后再顯示 #default 插槽的內(nèi)容。
#fallback:在異步依賴解析完成之前,<Suspense> 會顯示 #fallback 插槽中的內(nèi)容。
<Suspense>
<!-- 具有深層異步依賴的組件 -->
<template #fallback>
<Dashboard />
</template>
<!-- 在 #fallback 插槽中顯示 “正在加載中” -->
<template #fallback>
Loading...
</template>
</Suspense>
初始渲染
- 在初始渲染時,
<Suspense>會開始在內(nèi)存中渲染#default插槽的內(nèi)容。 - 如果
<Dashboard />組件(或者它的子組件樹)中包含異步依賴,比如異步組件加載或者async setup()函數(shù),那么在處理這些異步依賴的過程中,<Suspense>會進入掛起狀態(tài)。 - 例如,假設
<Dashboard />組件內(nèi)部,有一個異步請求數(shù)據(jù)的方法,在數(shù)據(jù)請求期間,<Suspense>就會進入掛起狀態(tài)。
掛起狀態(tài)
- 在掛起狀態(tài)期間,
<Suspense>會展示#fallback插槽中的內(nèi)容,以向用戶提供反饋,表明正在進行一些異步操作。 - 例如,當
<Dashboard />中的異步依賴正在處理時,用戶會看到Loading...。
完成狀態(tài)
- 當所有的異步依賴都完成后,
<Suspense>會進入完成狀態(tài),并顯示#default插槽的內(nèi)容。 - 如果在初次渲染時沒有遇到異步依賴,
<Suspense>會直接進入完成狀態(tài),直接展示#default插槽的內(nèi)容。
狀態(tài)切換條件:
官方說明:進入完成狀態(tài)后,只有當默認插槽的根節(jié)點被替換時,<Suspense> 才會回到掛起狀態(tài)。組件樹中新的更深層次的異步依賴不會造成 <Suspense> 回退到掛起狀態(tài)。
一旦進入完成狀態(tài),只有當 #default 插槽的根節(jié)點被替換時,<Suspense>才會回到掛起狀態(tài)。
- 例如,如果
<Dashboard />組件被完全替換為另一個具有異步依賴的組件,那么<Suspense>會重新進入掛起狀態(tài)。 - 當
<Suspense>從完成狀態(tài)因為#default插槽根節(jié)點被替換而回退到掛起狀態(tài)時,后備內(nèi)容不會立即展示。而是在等待新內(nèi)容和異步依賴完成的過程中,仍然展示之前#default插槽的內(nèi)容。
通過設置 timeout 屬性,配置過度行為:
- 在等待渲染新內(nèi)容耗時超過
timeout之后,<Suspense>將會切換為展示后備內(nèi)容。 - 設置
timeout: 0,一旦#default插槽的根節(jié)點被替換,就會立即顯示后備內(nèi)容。
如果只是在<Dashboard />組件的子組件樹中添加了新的異步依賴,<Suspense>不會回退到掛起狀態(tài)。
事件
<Suspense> 組件會觸發(fā)三個事件:pending、resolve 和 fallback。
@pending 是在進入掛起狀態(tài)時觸發(fā)。
- 當異步依賴開始加載時觸發(fā)。
- 可以在這個事件處理函數(shù)中顯示加載指示器等。
@resolve 是在 default 插槽完成獲取新內(nèi)容時觸發(fā)。
- 當所有異步依賴項解析完成時觸發(fā)。
- 可以在這個事件處理函數(shù)中執(zhí)行一些與異步依賴成功加載相關的邏輯。
@fallback 是在 fallback 插槽的內(nèi)容顯示時觸發(fā)。
- 當異步依賴沒有在規(guī)定時間內(nèi)(如果設置了
timeout)解析完成或者出現(xiàn)錯誤時觸發(fā)。 - 可以在這個事件處理函數(shù)中處理錯誤情況或顯示特定的 fallback 內(nèi)容。
錯誤處理
<Suspense> 組件自身目前還不提供錯誤處理,可以使用 errorCaptured 選項或者 onErrorCaptured() 鉤子,在使用到 <Suspense> 的父組件中捕獲和處理異步錯誤。
errorCaptured選項
在 Vue 組件的選項中,可以定義errorCaptured方法。這個方法會在捕獲到來自子孫組件(包括異步組件)的錯誤時被調(diào)用。
export default {
errorCaptured(error, component, info) {
// 處理錯誤的邏輯
console.log('Error captured:', error);
// 可以根據(jù)需要返回 false 來阻止錯誤繼續(xù)向上傳播
return false;
}
}
參數(shù)說明:
error參數(shù)是捕獲到的錯誤對象,component是拋出錯誤的組件實例,info是一個包含錯誤來源信息的字符串。
當在包含<Suspense>的父組件中定義了errorCaptured選項時,如果<Suspense>內(nèi)部的異步組件或異步操作拋出錯誤,errorCaptured方法將被調(diào)用。
<template>
<div>
<Suspense>
<template v-slot:default>
<Child />
</template>
<template v-slot:fallback>
<div>Loading...</div>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
errorCaptured(error, component, info) {
console.log('Error in Suspense:', error);
}
});
</script>
<script setup lang="ts">
import { Suspense } from 'vue';
import Child from './Child.vue';
</script>
onErrorCaptured ()鉤子(組合式 API)
在 Vue 3 的組合式 API 中,可以使用onErrorCaptured函數(shù)來注冊一個錯誤捕獲鉤子。
<script setup>
import { onErrorCaptured } from 'vue';
onErrorCaptured((error, component, info) => {
console.log('Error captured with onErrorCaptured:', error);
});
</script>
當在包含<Suspense>的父組件中使用onErrorCaptured鉤子時,如果<Suspense>內(nèi)部的異步組件或異步操作拋出錯誤,這個鉤子將被調(diào)用。
注意:
在errorCaptured方法或onErrorCaptured鉤子中,可以根據(jù)需要返回false來阻止錯誤繼續(xù)向上傳播。
如果不返回false或者返回true,錯誤將繼續(xù)向上傳遞給父組件的錯誤處理機制。
嵌套使用
當有多個類似于下方的異步組件 (常見于嵌套或基于布局的路由) 時:
<Suspense>
<component :is="DynamicAsyncOuter">
<component :is="DynamicAsyncInner" />
</component>
</Suspense>
<Suspense> 創(chuàng)建了一個邊界,它將如預期的那樣解析樹下的所有異步組件。
- 當更改
DynamicAsyncOuter時,<Suspense>會正確地等待它 - 當更改
DynamicAsyncInner時,嵌套的DynamicAsyncInner會呈現(xiàn)為一個空節(jié)點,直到它被解析為止 (而不是之前的節(jié)點或回退插槽)。
可以使用嵌套的方法來解決這個問題:
<Suspense>
<component :is="DynamicAsyncOuter">
<Suspense suspensible> <!-- 像這樣 -->
<component :is="DynamicAsyncInner" />
</Suspense>
</component>
</Suspense>
設置 suspensible 屬性后,所有異步依賴項處理都會交給父級 <Suspense> (包括發(fā)出的事件),而內(nèi)部 <Suspense> 僅充當依賴項解析和修補的另一個邊界。
如果不設置 suspensible 屬性,內(nèi)部的 <Suspense> 將被父級 <Suspense> 視為同步組件。
這意味著它將會有自己的回退插槽,如果兩個 Dynamic 組件同時被修改,則當子 <Suspense> 加載其自己的依賴關系樹時,可能會出現(xiàn)空節(jié)點和多個修補周期。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Vue中使用jsencrypt進行RSA非對稱加密的操作方法
這篇文章主要介紹了Vue中使用jsencrypt進行RSA非對稱加密,在這里需要注意要加密的數(shù)據(jù)必須是字符串,對Vue?RSA非對稱加密相關知識感興趣的朋友一起看看吧2022-04-04
element-ui Upload上傳組件動態(tài)配置action方式
這篇文章主要介紹了element-ui Upload上傳組件動態(tài)配置action方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
五分鐘教你使用vue-cli3創(chuàng)建項目(新手入門)
本文主要介紹了五分鐘教你使用vue-cli3創(chuàng)建項目,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
elementUI給el-tabs/el-tab-pane添加圖標效果實例
這篇文章主要給大家介紹了關于elementUI給el-tabs/el-tab-pane添加圖標效果實例的相關資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用elementUI具有一定的參考學習價值,需要的朋友可以參考下2023-07-07

