Provide / Inject
Si assume che tu abbia già letto le Basi dei componenti. Leggi prima quello se sei nuovo al concetto di componente.
Prop drilling
Solitamente, quando abbiamo bisogno di passare dati dal genitore a un componente figlio, utilizziamo props. Tuttavia, immagina il caso in cui abbiamo un albero di componenti ampio e un componente profondamente nidificato ha bisogno di qualcosa da un componente antenato distante. Con solo le props, dovremmo passare la stessa prop lungo l'intera catena genitore:
Nota che anche se il componente <Footer>
potrebbe non essere interessato a queste props, deve comunque dichiararle e passarle in avanti affinché <DeepChild>
possa accedervi. Se c'è una catena genitore più lunga, più componenti sarebbero influenzati lungo il percorso. Questo viene chiamato "props drilling" e sicuramente non è divertente da gestire.
Possiamo risolvere il problema del props drilling con provide
e inject
. Un componente genitore può fungere da 'fornitore di dipendenze' per tutti i suoi discendenti. Qualsiasi componente nell'albero dei discendenti, indipendentemente da quanto sia profondo, può iniettare dipendenze fornite dai componenti nella sua catena genitore.
Provide
Per fornire dati ai discendenti di un componente, utilizza la funzione provide()
:
vue
<script setup>
import { provide } from 'vue'
provide(/* chiave */ 'messaggio', /* valore */ 'ciao!')
</script>
Se non stai utilizzando <script setup>
, assicurati che provide()
venga chiamato in modo sincrono all'interno di setup()
:
js
import { provide } from 'vue'
export default {
setup() {
provide(/* chiave */ 'messaggio', /* valore */ 'ciao!')
}
}
La funzione provide()
accetta due argomenti. Il primo argomento è chiamato chiave di iniezione, che può essere una stringa o un Symbol
. La chiave di iniezione è utilizzata dai componenti discendenti per cercare il valore desiderato da iniettare. Un singolo componente può chiamare provide()
più volte con diverse chiavi di iniezione per fornire valori diversi.
Il secondo argomento è il valore fornito. Il valore può essere di qualsiasi tipo, inclusi stati reattivi come i ref:
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
Fornire valori reattivi consente ai componenti discendenti che utilizzano il valore fornito di stabilire una connessione reattiva con il componente fornitore.
Provide a livello app
Oltre a fornire dati in un componente, possiamo anche fornirli a livello di app:
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* chiave */ 'messaggio', /* valore */ 'ciao!')
Le forniture a livello di app sono disponibili per tutti i componenti renderizzati nell'app. Questo è particolarmente utile quando si scrivono plugins, poiché i plugin di solito non possono fornire valori utilizzando componenti.
Inject
Per iniettare i dati forniti da un componente antenato, utilizza la funzione inject()
:
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
Se il valore fornito è un ref, verrà iniettato così com'è e non verrà srotolato automaticamente. Questo consente al componente iniettore di mantenere la connessione reattiva con il componente fornitore.
Esempio completo di provide + inject con reattività
Ancora una volta, se non stai usando <script setup>
, inject()
dovrebbe essere chiamato in modo sincrono all'interno di setup()
:
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
Valori predefiniti di Inject
Per impostazione predefinita inject
assume che la chiave di iniezione sia fornita da qualche parte nella catena di genitori. Nel caso in cui la chiave non sia fornita, verrà emesso un avviso durante l'esecuzione.
Se vogliamo rendere una proprietà iniettata funzionante con fornitori opzionali, dobbiamo dichiarare un valore predefinito, simile alle props:
js
// `value` sarà "default value"
// se nessun dato corrispondente a "message" è stato fornito
const value = inject('message', 'default value')
In alcuni casi, il valore predefinito potrebbe dover essere creato chiamando una funzione o istanziando una nuova classe. Per evitare calcoli o effetti collaterali non necessari nel caso in cui il valore opzionale non venga utilizzato, possiamo utilizzare una funzione factory per creare il valore predefinito:
js
const value = inject('key', () => new ExpensiveClass(), true)
Il terzo parametro indica che il valore predefinito dovrebbe essere trattato come una funzione factory.
Lavorare con la reattività
Quando si utilizzano valori di provide / inject reattivi, è consigliabile mantenere qualsiasi mutazione allo stato reattivo all'interno del provider quando possibile. Ciò garantisce che lo stato fornito e le sue eventuali mutazioni siano collocate nello stesso componente, facilitandone la manutenzione in futuro.
Ci possono essere momenti in cui è necessario aggiornare i dati da un componente injector. In tali casi, raccomandiamo di fornire una funzione responsabile per la mutazione dello stato:
vue
<!-- all'interno del componente provider -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
vue
<!-- all'interno del componente injector -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
Infine, puoi avvolgere il valore fornito con readonly()
se desideri garantire che i dati passati attraverso provide
non possano essere modificati dal componente iniettore.
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
Lavorare con symbol keys
Finora, abbiamo utilizzato chiavi di iniezione di tipo stringa negli esempi. Se stai lavorando in un'applicazione di grandi dimensioni con molti fornitori di dipendenze o stai creando componenti destinate ad essere utilizzate da altri sviluppatori, è meglio utilizzare chiavi di iniezione di tipo Symbol per evitare potenziali collisioni.
Si consiglia di esportare i Symbols in un file dedicato:
js
// keys.js
export const myInjectionKey = Symbol()
js
// nel componente provider
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* dati da fornire */
})
js
// nel componente iniettore
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
Guarda anche: Tipizzare Provide / Inject