Suspense
Experimentální funkce
<Suspense>
je experimentální funkce. Není zaručeno, že bude zachována, a API se může změnit dříve, než se stane stabilní součástí Vue.
<Suspense>
je vestavěná komponenta pro orchestraci asynchronních závislostí ve stromu komponent. Může vykreslit stav načítání, zatímco čeká na vyřešení několika vnořených asynchronních závislostí.
Asynchronní závislosti
Pro vysvětlení problému, který se <Suspense>
snaží řešit, a jak s těmito asynchronními závislostmi interaguje, si představte následující hierarchii komponent:
<Suspense>
└─ <Dashboard>
├─ <Profile>
│ └─ <FriendStatus> (komponenta s `async setup()`)
└─ <Content>
├─ <ActivityFeed> (`async` komponenta)
└─ <Stats> (`async` komponenta)
Ve stromu komponent je více vnořených komponent, jejichž vykreslování závisí na nějakém asynchronním zdroji, který je třeba vyřešit jako první. Bez <Suspense>
by každý z nich musel zvládnout zobrazení svého vlastního průběhu načítání a chybových stavů. V krajním případě bychom mohli na stránce vidět tři samostatné indikátory načítání a obsah zobrazený postupně v různých časech.
<Suspense>
nám dává schopnost zobrazit průběh načítání a chybové stavy pouze na nejvyšší úrovni, zatímco čekáme na vyřešení vnořených asynchronních závislostí.
Jsou dva typy asynchronních závislostí, na které může <Suspense>
čekat:
Komponenty s asynchronní
setup()
sekcí. K nim patří<script setup>
komponenty s top‑levelawait
výrazy.
Asynchronní setup()
Možnost setup()
v Composition API může být asynchronní:
js
export default {
async setup() {
const res = await fetch(...)
const posts = await res.json()
return {
posts
}
}
}
Při použití <script setup>
vyrobí přítomnost top-level await
výrazu z komponenty automaticky asynchronní závislost:
vue
<script setup>
const res = await fetch(...)
const posts = await res.json()
</script>
<template>
{{ posts }}
</template>
Asynchronní komponenty
Asynchronní komponenty jsou přirozně „suspenzovatelné“. To znamená, že pokud má komponenta v rodičovském řetězci <Suspense>
, bude s ní zacházeno jako s asynchronní závislostí této <Suspense>
. V tomto případě bude stav načítání ovládán <Suspense>
a vlastní nastavení načítání, chyb, zpoždění a časového limitu v komponentě budou ignorovány.
Asynchronní komponenta se může z kontroly nadřazené <Suspense>
vyvázat a kontrolovat svůj vlastní stav načítání pomocí nastavení suspensible: false
ve vlastnostech komponenty.
Stav načítání
Komponenta <Suspense>
má dva sloty: #default
a #fallback
. Oba sloty povolují pouze jeden element bezprostředního potomka. Element ve výchozím (default) slotu je zobrazen, pokud je to možné. Poku není, je místo toho zobrazen element v záložním (fallback) slotu.
template
<Suspense>
<!-- komponenta s vnořenými async závislostmi -->
<Dashboard />
<!-- stav načítání pomocí #fallback slotu -->
<template #fallback>
Načítání...
</template>
</Suspense>
Při úvodním vykreslení <Suspense>
zobrazí obsah svého výchozího (default) slotu v paměti. Pokud se během procesu objeví nějaké asynchronní závislosti, přejde se do stavu pending (čekající). Během čekání se zobrazí záložní (fallback) obsah. Poté, co jsou vyřešeny všechny zjištěné asynchronní závislosti, <Suspense>
přejde do stavu resolved (vyřešeno) a zobrazí se konečný obsah výchozího slotu.
Pokud se během úvodního vykreslení na žádné asynchronní závislosti nenarazí, <Suspense>
přejde rovnou do stavu vyřešeno.
Jakmile je ve stavu vyřešeno, <Suspense>
se vrátí do čekajícího (pending) stavu pouze tehdy, pokud je nahrazen kořenový element #default
slotu. Nové asynchronní závislosti vnořené hlouběji do stromu nezpůsobí, že se <Suspense>
vrátí do čekajícího stavu.
Když dojde k aktualizaci, záložní (fallback) obsah se nezobrazí okamžitě. Místo toho <Suspense>
zobrazí předchozí obsah #default
slotu, zatímco čeká na vyřešení nového obsahu a jeho asynchronních závislostí. Toto chování lze konfigurovat pomocí vlastnosti timeout
: <Suspense>
se na záložní obsah přepne, pokud vykreslení nového výchozího obsahu trvá déle než timeout
. Hodnota timeout: 0
způsobí, že se záložní obsah zobrazí okamžitě po nahrazení výchozího obsahu.
Události (Events)
Komponenta <Suspense>
emituje 3 události: pending
, resolve
a fallback
. Událost pending
nastává při přechodu do čekajícího stavu. Událost resolve
se volá, když je dokončeno vyhodnocování nového obsahu v #default
slotu. Událost fallback
je emitována v okamžiku zobrazení #fallback
obsahu.
Tyto události lze použít například k zobrazení indikátoru načítání před starým obsahem DOM během vykreslování nových komponent.
Obsluha chyb
<Suspense>
momentálně obsluhu chyb sama o sobě nenabízí - nicméně můžete použít možnost errorCaptured
nebo onErrorCaptured()
hook k zachycení a zpracování asynchronních chyb v komponentě nadřazené <Suspense>
.
Kombinace s ostatními komponentami
Je běžné používat <Suspense>
v kombinaci s <Transition>
a <KeepAlive>
komponentami. Pořadí vnoření je důležté, aby všechny fungovaly správně.
Dále jsou tyto komponenty často používány ve spojení s <RouterView>
komponentou z Vue Router.
Následující příklad ukazuje, jak tyto komponenty správně vnořit, aby se všechny chovaly podle očekávání. Pro jednodušší kombinace můžete odebrat části, které nepotřebujete:
template
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- hlavní obsah -->
<component :is="Component"></component>
<!-- stav načítání -->
<template #fallback>
Načítání...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
Vue Router má vestavěnou podporu pro „lazy“ načítání komponent pomocí dynamických importů. Ty se od asynchronních komponent liší a v současnosti na ně <Suspense>
nereaguje. Stále však mohou mít další asynchronní komponenty jako potomky a ty mohou <Suspense>
vyvolat obvyklým způsobem.
Vnořené Suspense
- Podporováno až od verze 3.3+
Když máme více asynchronních komponent (což je běžné ve vnořených cestách (routes) nebo v cestách založených na layoutech) jako například tyto:
template
<Suspense>
<component :is="DynamicAsyncOuter">
<component :is="DynamicAsyncInner" />
</component>
</Suspense>
<Suspense>
vytvoří v souladu s očekáváním ohraničení, které vyřeší všechny asynchronní komponenty v celém stromě závislostí. Pokud změníme DynamicAsyncOuter
, <Suspense>
na ni správně počká. Ale když změníme DynamicAsyncInner
, vnořená DynamicAsyncInner
vykreslí prázný element, dokud nebude vyřešena (místo předchozí komponenty nebo fallback slotu).
Abychom to vyřešili, můžeme přidat vnořenou suspense k obsluze aktualizace vnořené komponenty tímto způsobem:
template
<Suspense>
<component :is="DynamicAsyncOuter">
<Suspense suspensible> <!-- zde -->
<component :is="DynamicAsyncInner" />
</Suspense>
</component>
</Suspense>
Pokud nenastavíte vlastnost suspensible
, vnitřní <Suspense>
bude rodičovskou <Suspense>
považována za synchronní komponentu. To znamená, že má svůj vlastní fallback slot a když se obě Dynamic
komponenty změní najednou, mohou se objevit prázdné elementy a spustit více aktualizčních cyklů, zatímco vnořená <Suspense>
načítá svůj strom závislostí. Což nemusí být žádoucí. Když je suspensible
nastaveno, obsluha asynchronního načítání je předána rodičovské <Suspense>
(vč. emitovaných událostí) a vnitřní <Suspense>
slouží jen jako další ohraničení pro řešení závislostí a aktualizace.
Související