Attributi trasferibili (fallthrough)
Si assume che tu abbia già letto le Basi dei componenti. Leggi prima quello se sei nuovo al concetto di componente.
Ereditarietà degli attributi
Un "attributo fallthrough" è un attributo o un listener v-on
che viene passato a un componente, ma non è dichiarato esplicitamente nelle props o negli emits del componente ricevente. Esempi comuni di questo includono gli attributi class
, style
, e id
.
Quando un componente renderizza un singolo elemento radice, gli attributi fallthrough verranno automaticamente aggiunti agli attributi dell'elemento radice. Ad esempio, dato un componente <MyButton>
con il seguente template:
template
<!-- template di <MyButton> -->
<button>cliccami</button>
E un genitore che utilizza questo componente con:
template
<MyButton class="large" />
Il DOM renderizzato finale sarebbe:
html
<button class="large">cliccami</button>
Qui, <MyButton>
non ha dichiarato class
come prop accettata. Pertanto, class
è trattata come attributo fallthrough e viene automaticamente aggiunta all'elemento radice di <MyButton>
.
Unione di class
e style
Se l'elemento radice del componente figlio ha già attributi class
o style
esistenti, essi verranno uniti con i valori di class
e style
ereditati dal genitore. Supponiamo di modificare il template di <MyButton>
nell'esempio precedente in questo modo:
template
<!-- template di <MyButton> -->
<button class="btn">cliccami</button>
Quindi il DOM renderizzato finale diventerebbe:
html
<button class="btn large">cliccami</button>
Eredità dei Listener v-on
La stessa regola si applica ai listener degli eventi v-on
:
template
<MyButton @click="onClick" />
Il listener click
verrà aggiunto all'elemento radice di <MyButton>
, cioè all'elemento nativo <button>
. Quando viene fatto clic sull'elemento nativo <button>
, verrà innescato il metodo onClick
del componente genitore. Se l'elemento nativo <button>
ha già un listener click
legato a v-on
, allora entrambi i listener verranno innescati.
Ereditarietà dei componenti nidificati
Se un componente renderizza un altro componente come suo nodo radice, ad esempio, abbiamo refactorato <MyButton>
per renderizzare un <BaseButton>
come sua radice:
template
<!-- template di <MyButton/> che semplicemente renderizza un altro componente -->
<BaseButton />
Poi gli attributi fallthrough ricevuti da <MyButton>
verranno automaticamente inoltrati a <BaseButton>
.
Nota che:
Gli attributi inoltrati non includono alcun attributo dichiarato come prop, né listener
v-on
degli eventi dichiarati da<MyButton>
- in altre parole, le props e i listener dichiarati sono stati "consumati" da<MyButton>
.Gli attributi inoltrati possono essere accettati come props da
<BaseButton>
, se dichiarati da esso.
Disattivare l'ereditarietà degli attributi
Se non desideri che un componente erediti automaticamente gli attributi, puoi impostare inheritAttrs: false
nelle opzioni del componente.
A partire dalla versione 3.3 puoi anche utilizzare defineOptions
direttamente in <script setup>
:
vue
<script setup>
defineOptions({
inheritAttrs: false
})
// ...logic del setup
</script>
Lo scenario comune per disattivare l'ereditarietà degli attributi è quando gli attributi devono essere applicati ad altri elementi oltre al nodo radice. Impostando l'opzione inheritAttrs
su false
, puoi avere pieno controllo su dove dovrebbero essere applicati gli attributi fallthrough.
Questi attributi fallthrough possono essere accessi direttamente nelle espressioni del template come $attrs
:
template
<span>Attributi fallthrough: {{ $attrs }}</span>
L'oggetto $attrs
include tutti gli attributi che non sono dichiarati dalle opzioni props
o emits
del componente (ad esempio, attributi come class
, style
, v-on
, etc.).
Alcune note:
A differenza delle props, gli attributi fallthrough mantengono la loro capitalizzazione originale in JavaScript, quindi per accedere ad un attributo come
foo-bar
devi usare$attrs['foo-bar']
.Un listener
v-on
come@click
verrà esposto sull'oggetto come una funzione sotto$attrs.onClick
.
Utilizzando l'esempio del componente <MyButton>
dalla sezione precedente - a volte potrebbe essere necessario avvolgere l'effettivo elemento <button>
con un ulteriore <div>
per scopi stilistici:
template
<div class="btn-wrapper">
<button class="btn">cliccami</button>
</div>
Vogliamo che tutti gli attributi fallthrough come class
e i listener v-on
vengano applicati all'elemento <button>
interno, non al <div>
. Possiamo ottenere questo risultato con inheritAttrs: false
e v-bind="$attrs"
:
template
<div class="btn-wrapper">
<button class="btn" v-bind="$attrs">cliccami</button>
</div>
Ricorda che v-bind
senza un argomento lega tutte le proprietà di un oggetto come attributi dell'elemento di destinazione.
Ereditarietà degli attributi su più nodi radice
A differenza dei componenti con un singolo nodo radice, i componenti con più nodi radice non hanno un comportamento automatico di ereditarietà degli attributi. Se $attrs
non viene legato esplicitamente, verrà emesso un avviso a runtime.
template
<CustomLayout id="custom-layout" @click="changeValue" />
Se <CustomLayout>
ha il seguente template con più nodi radice, verrà emesso un avviso poiché Vue non può essere certo su dove applicare gli attributi fallthrough:
template
<header>...</header>
<main>...</main>
<footer>...</footer>
L'avviso verrà soppresso se $attrs
viene legato esplicitamente:
template
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
Accesso agli attributi fallthrough in JavaScript
Se necessario, puoi accedere agli attributi fallthrough di un componente in <script setup>
utilizzando l'API useAttrs()
:
vue
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
Se non stai usando <script setup>
, attrs
verrà esposto come proprietà del contesto setup()
:
js
export default {
setup(props, ctx) {
// gli attributi fallthrough sono esposti come ctx.attrs
console.log(ctx.attrs)
}
}
Tieni presente che, sebbene l'oggetto attrs
qui rifletta sempre gli attributi fallthrough più recenti, non è reattivo (per motivi di prestazioni). Non puoi utilizzare i watcher per osservarne le modifiche. Se hai bisogno di reattività, utilizza una prop. In alternativa, puoi utilizzare onUpdated()
per eseguire effetti collaterali con l'ultimo attrs
in ogni aggiornamento.