vue3中的內(nèi)置組件<Suspense>用法
官方文檔:
官方提示:
<Suspense>是一項(xiàng)實(shí)驗(yàn)性功能。它不一定會(huì)最終成為穩(wěn)定功能,并且在穩(wěn)定之前相關(guān) API 也可能會(huì)發(fā)生變化。<Suspense>是一個(gè)內(nèi)置組件,用來(lái)在組件樹(shù)中協(xié)調(diào)對(duì)異步依賴(lài)的處理。它讓我們可以在組件樹(shù)上層等待下層的多個(gè)嵌套異步依賴(lài)項(xiàng)解析完成,并可以在等待時(shí)渲染一個(gè)加載狀態(tài)。
異步依賴(lài)
要了解 <Suspense> 所解決的問(wèn)題和它是如何與異步依賴(lài)進(jìn)行交互的,需要了解什么是異步依賴(lài)。
<Suspense>主要用于包裹異步組件,這些組件可能需要進(jìn)行異步數(shù)據(jù)獲取或其他耗時(shí)的操作:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus>(組件有異步的 setup())
└─ <Content>
├─ <ActivityFeed> (異步組件)
└─ <Stats>(異步組件)
在這個(gè)組件樹(shù)中有多個(gè)嵌套組件,要渲染出它們,首先得解析一些異步資源。
如果沒(méi)有 <Suspense>組件:
- 以vue2 項(xiàng)目為例:這個(gè)組件樹(shù)中的全部組件,都需要處理各自的加載、報(bào)錯(cuò)和完成狀態(tài)。
- 在網(wǎng)絡(luò)不好或數(shù)據(jù)量過(guò)大的時(shí)候,可以看到頁(yè)面上有多個(gè)加載狀態(tài),它們?cè)诓煌臅r(shí)間加載完成,頁(yè)面顯示出內(nèi)容。
有了 <Suspense> 組件后:
- 可以在等待整個(gè)多層級(jí)組件樹(shù)中的各個(gè)異步依賴(lài)獲取結(jié)果時(shí),在頂層展示出加載中或加載失敗的狀態(tài)。
<Suspense> 可以等待的異步依賴(lài)有兩種:
- 帶有異步
setup()鉤子的組件。這也包含了使用<script setup>時(shí)有頂層await表達(dá)式的組件。 - 異步組件。
async setup()
組合式 API 中組件的 setup() 鉤子可以是異步的:
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
<script setup>語(yǔ)法糖
在<script setup>語(yǔ)法糖中,開(kāi)發(fā)者無(wú)法在前面添加async。
如果使用 <script setup>,那么頂層 await 表達(dá)式會(huì)自動(dòng)讓該組件成為一個(gè)異步依賴(lài):
<!-- 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()) // 一個(gè)Promise
</script>
在父組件中直接引用Child.vue組件:
<template>
<div class="home-wrap">
<h1>主頁(yè)----父組件</h1>
<Child />
</div>
</template>
<script setup lang="ts">
import Child from './Child.vue';
</script>
頁(yè)面沒(méi)有顯示Child.vue的內(nèi)容,并且在控制臺(tá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ù)返回了一個(gè)promise,但是在父組件樹(shù)中沒(méi)有找到<Suspense> 邊界。帶有async setup()的組件必須嵌套在<Suspense> 中才能呈現(xiàn)。
按照提示修改父組件:
<template>
<div class="home-wrap">
<h1>主頁(yè)----父組件</h1>
<Suspense>
<!-- 異步任務(wù)ok后 -->
<template v-slot:default>
<Child />
</template>
<!-- 異步任務(wù)未做完時(shí) -->
<template v-slot:fallback>
<div>請(qǐng)求中...</div>
</template>
</Suspense>
</div>
</template>
<script setup lang="ts">
import { Suspense } from 'vue';
import Child from './Child.vue';
</script>
頁(yè)面成功展示Child.vue的內(nèi)容:

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

