Provide / Inject
Cette page suppose que vous avez déjà lu les principes fondamentaux des composants. Lisez-les d'abord si vous débutez avec les composants.
Passage de props en profondeur (Prop Drilling)
Habituellement, lorsque nous devons transmettre des données du parent à un composant enfant, nous utilisons des props. Cependant, imaginez le cas où nous avons un arbre de composants important, et qu'un composant profondément imbriqué aurait besoin d'accéder à des informations d'un de ses composants parents. Avec seulement des props, nous devrions passer la même prop sur toute la chaîne composants parents :
Notez que bien que le composant <Footer>
n'utilise pas du tout ces props, il doit malgré tout les déclarer et les transmettre uniquement afin que <DeepChild>
puisse y accéder. S'il y a une chaîne de composants parents plus longue, encore plus de composants seront affectés par le problème. C'est ce qu'on appelle le "props drilling" et ce n'est certainement pas amusant à gérer.
Nous pouvons résoudre le "props drilling" avec provide
and inject
. Un composant parent peut servir de fournisseur de dépendances pour tous ses descendants. Tout composant enfant de l'arborescence, quelle que soit sa profondeur, peut injecter des dépendances fournies par des composants présent dans sa chaîne de composants parents.
Provide
Pour fournir des données aux descendants d'un composant, utilisez la fonction provide()
:
vue
<script setup>
import { provide } from 'vue'
provide(/* clé */ 'message', /* valeur */ 'hello!')
</script>
Si vous n'utilisez pas <script setup>
, assurez-vous que provide()
est appelé de manière synchrone dans setup()
:
js
import { provide } from 'vue'
export default {
setup() {
provide(/* clé */ 'message', /* valeur */ 'hello!')
}
}
La fonction provide()
accepte deux arguments. Le premier argument est appelé la clé d'injection, qui peut être une chaîne de caractères ou un Symbol
. La clé d'injection est utilisée par les composants descendants pour rechercher la valeur souhaitée à injecter. Un composant peut appeler provide()
plusieurs fois avec différentes clés d'injection pour fournir différentes valeurs.
Le deuxième argument est la valeur fournie. La valeur peut être de n'importe quel type, y compris un état réactif tel que des refs :
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
Fournir des valeurs réactives permet aux composants descendants qui les utilisent d'établir une connexion réactive au composant fournisseur.
Provide au niveau de l'application
En plus de fournir des données dans un composant, nous pouvons également fournir des données au niveau de l'application :
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* clé */ 'message', /* valeur */ 'hello!')
Les données fournies au niveau de l'application sont disponibles pour tous les composants rendus dans l'application. Ceci est particulièrement utile lors de l'écriture de plugins, car les plugins ne seraient généralement pas en mesure de fournir des valeurs à l'aide de composants.
Inject
Pour injecter des données fournies par un composant parent, utilisez la fonction inject()
:
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
Si la valeur fournie est une référence, elle sera injectée telle quelle et ne sera pas automatiquement exposée. Cela permet au composant injecteur de conserver la connexion de réactivité au composant fournisseur.
Exemple complet provide + inject avec réactivité
Encore une fois, si vous n'utilisez pas <script setup>
, inject()
ne doit être appelé que de manière synchrone dans setup()
:
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
Valeurs par défaut lors de l'injection
Par défaut, inject
suppose que la clé injectée est fournie quelque part dans la chaîne de composants parents. Dans le cas où la clé n'est pas fournie, il y aura un message d'avertissement lors de l'exécution.
Si nous voulons faire fonctionner une propriété injectée avec des fournisseurs facultatifs, nous devons déclarer une valeur par défaut, de la même manière qu'avec les props :
js
// `value` sera "default value"
// si aucune donnée correspondant à la clé "message" n'a été fournie
const value = inject('message', 'default value')
Dans certains cas, la valeur par défaut peut devoir être créée en appelant une fonction ou en instanciant une nouvelle classe. Pour éviter des calculs inutiles ou des effets secondaires si la valeur facultative n'est pas utilisée, nous pouvons utiliser une fonction factory pour créer la valeur par défaut :
js
const value = inject('key', () => new ExpensiveClass())
Injections réactives
Lors de l'utilisation de valeurs de réactives avec provide / inject, il est recommandé de conserver toutes les mutations d'un état réactif à l'intérieur du fournisseur dans la mesure du possible. Cela garantit que l'état fourni et ses éventuelles mutations sont localisés dans le même composant, ce qui facilite sa maintenabilité à l'avenir.
Il peut arriver que nous ayons besoin de mettre à jour les données depuis un composant réalisant l'injection. Dans de tels cas, nous vous recommandons de fournir une fonction responsable de la mutation de l'état :
vue
<!-- à l'intérieur du composant fournisseur -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
vue
<!-- dans le composant injecteur -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
Enfin, vous pouvez encapsuler la valeur fournie avec readonly()
si vous souhaitez vous assurer que les données transmises via provide
ne puissent pas être mutées par le composant réalisant l'injection.
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
Injection avec des Symbols en tant que clés
Jusqu'à présent, nous avons utilisé dans les exemples des clés d'injection qui étaient des chaînes de caractères. Si vous travaillez dans une application de taille importante avec de nombreux fournisseurs de dépendances, ou si vous créez des composants qui seront utilisés par d'autres développeurs, il est préférable d'utiliser des clés d'injection utilisant des Symbols pour éviter les collisions potentielles.
Il est recommandé d'exporter les Symbols dans un fichier dédié :
js
// keys.js
export const myInjectionKey = Symbol()
js
// dans le composant fournisseur
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* données à fournir */
})
js
// dans le composant injecteur
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
Voir aussi : Définir le type des données avec Provide / Inject