"Susceptible" signifie remplaçable par un contenu de substitution pendant que le parent <Suspense>
résout async
les composants enfants trouvés dans son <template #default>
.
Le concept est emprunté à l'outil de React Suspense API .
Plus en détail, <Suspense>
est un composant intégré de Vue 3 qui rend une <template #fallback>
au lieu de la <template #default>
jusqu'à ce que tous les async
les composants enfants du modèle par défaut sont résolus.
Afin d'être suspendu si le rendu d'un composant doit dépendre d'une promesse :
- être chargé en utilisant
() => import('some/path')
- ou utiliser un
async/await
(ou toute autre forme de syntaxe de Promise) dans ses setup
fonction
Un composant suspensible est suspendu lorsqu'il est inclus dans un <Suspense>
tandis que son parent <Suspense>
n'a pas résolu tous ses composants suspensifs, même si le composant suspensif lui-même a déjà été résolu.
Évidemment, <Suspense>
Les composants eux-mêmes sont suspensibles et la suspension peut être imbriquée.
Voici un explication plus détaillée sur <Suspense>
dans Vue 3.
Entre autres usages, <Suspence>
offre un moyen élégant et intuitif de résoudre le problème courant qui consiste à devoir envelopper les composants et les modèles enfants dans des fichiers v-if
la protection contre les propriétés inexistantes sur des données qui n'ont pas encore été chargées.
Un exemple typique de Vue 2 :
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('render-items', {
props: ['items'],
template: `<table>
<tr>
<th>Id</th>
<th>User Id</th>
<th>Title</th>
</tr>
<tr v-for="(item, key) in items" :key="key">
<td v-text="item.id"></td>
<td v-text="item.userId"></td>
<td v-text="item.title"></td>
</tr>
</table>`
});
new Vue({
el: '#app',
data: () => ({
items: []
}),
computed: {
hasData() {
return this.items.length;
}
},
async created() {
const items = await fetch('https://jsonplaceholder.typicode.com/posts')
.then(r => r.json());
this.items = items;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<render-items :items="items" v-if="hasData"></render-items>
<template v-else>loading...</template>
</div>
Même exemple (plus ou moins) dans Vue 3, en utilisant <Suspense>
y async setup
:
const RenderItems = Vue.defineComponent({
async setup() {
const items = await fetch('https://jsonplaceholder.typicode.com/posts')
.then(r => r.json());
return Vue.reactive({ items });
},
template: `<table>
<tr>
<th>Id</th>
<th>User Id</th>
<th>Title</th>
</tr>
<tr v-for="(item, key) in items" :key="key">
<td v-text="item.id"></td>
<td v-text="item.userId"></td>
<td v-text="item.title"></td>
</tr>
</table>`
});
const App = { components: { RenderItems }};
Vue.createApp(App).mount('#app');
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<div id="app">
<Suspense>
<template #default>
<render-items></render-items>
</template>
<template #fallback>
loading...
</template>
</Suspense>
</div>
Un avantage majeur est que dans l'exemple de Vue 3, nous pouvons contenir le récupérateur de données (et les données) dans le composant enfant. Ceci n'est pas possible dans Vue 2, car :
- le sous-composant n'est créé qu'après le chargement des données.
- le parent doit savoir quand la condition a changé (il doit donc avoir accès à la condition réelle) afin de passer du rendu du contenu de remplacement à celui du composant enfant.
La façon la plus simple de le faire dans Vue 2 est en fait de charger les données dans le parent et de passer le résultat au composant enfant, via les props. Si vous avez beaucoup de sous-composants, ce modèle peut devenir désordonné.
Dans Vue 3, la responsabilité du chargement des données et de la vérification de la condition peut reposer entièrement sur le composant enfant. Le parent n'a pas besoin d'accéder à la condition réelle.
Tout comme <template>
, <Suspense>
ne crée pas d'élément DOM.
Dans l'exemple Vue 3 ci-dessus, <RenderItems />
est suspensible.