Tutorial VueJS 3: comunicare gli eventi con defineEmits
verificato il 2 Settembre 2026 · 3 min
Un componente figlio segnala un cambiamento al genitore emettendo un evento: defineEmits lo dichiara, emit('add-to-cart', id) lo invia e il genitore lo ascolta con @add-to-cart. Le props scendono, gli eventi risalgono.
Le props scendono dal genitore al figlio. Per far risalire un’informazione, un componente emette un evento e il genitore lo ascolta, esattamente come ascolterebbe un clic.
Il problema
Dopo la suddivisione del capitolo 8, il carrello vive in App.vue e il pulsante in ProductDisplay.vue. Il figlio non può modificare cart: non vi ha accesso, e un componente non deve scrivere nello stato di un altro.
La formulazione giusta non è «il figlio aggiunge al carrello», ma «il figlio annuncia che è stato fatto clic, il genitore decide che farne».
Dichiarare ed emettere
Nel figlio dichiara gli eventi emessi, poi emetti:
<script setup>
const emit = defineEmits(['add-to-cart'])
function addToCart() {
emit('add-to-cart')
}
</script>
<template>
<button
class="button"
:class="{ disabledButton: !inStock }"
:disabled="!inStock"
@click="addToCart"
>
Ajouter au panier
</button>
</template>defineEmits è una macro del compilatore, come defineProps: nessun import. Restituisce la funzione che serve a emettere.
Dichiarare gli eventi non è obbligatorio, ma è utile: l’elenco documenta il contratto del componente, l’editor lo completa in automatico e Vue avverte se emetti un nome non dichiarato.
Ascoltare lato genitore
Un evento personalizzato si ascolta con la stessa sintassi di un evento nativo:
<script setup>
import { ref } from 'vue'
import ProductDisplay from './components/ProductDisplay.vue'
const cart = ref(0)
function updateCart() {
cart.value += 1
}
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay :premium="premium" @add-to-cart="updateCart" />
</template>Fai clic: il contatore riparte. Il pulsante sta nel figlio, il contatore nel genitore, e nessuno dei due conosce lo stato dell’altro.
Trasmettere un dato
Un carrello realistico ricorda che cosa ci metti dentro, non solo quanto. Passa l’identificativo della variante come secondo argomento di emit:
<script setup>
function addToCart() {
emit('add-to-cart', variants.value[selectedVariant.value].id)
}
</script>Il genitore lo riceve come parametro della sua funzione:
<script setup>
const cart = ref([])
function updateCart(id) {
cart.value.push(id)
}
</script>
<template>
<div class="cart">Panier ({{ cart.length }})</div>
<ProductDisplay :premium="premium" @add-to-cart="updateCart" />
</template>cart è diventato un array, quindi la visualizzazione usa cart.length. Aggiungi più volte la T-shirt blu, poi verifica negli strumenti di sviluppo di Vue che l’array si riempia davvero di 2234.
Per più informazioni, emetti un oggetto invece di una lunga lista di argomenti:
emit('add-to-cart', { id: variant.id, taille: 'M', quantite: 1 })Il nome degli eventi
Scrivi il nome in kebab-case: add-to-cart, non addToCart. Gli attributi HTML non distinguono le maiuscole, un template scritto direttamente in una pagina riceverebbe @addtocart e il listener non scatterebbe mai. In un file .vue Vue sistema da sé le maiuscole, ma tanto vale prendere subito l’abitudine giusta.
Un evento personalizzato si propaga solo al genitore diretto. Se tre livelli di componenti separano chi emette da chi deve ricevere, va riemesso a ogni piano. Quando diventa pesante, è il segnale che serve uno stato condiviso: è esattamente il ruolo di Pinia.
Il senso di marcia
Una recensione che arriva per questa strada può poi essere animata alla comparsa.
Tieni a mente due frasi: le props scendono, gli eventi risalgono. Il genitore possiede il dato, il figlio ne chiede la modifica. È questo che rende il flusso prevedibile e l’origine di un cambiamento sempre tracciabile.
Gli eventi si dichiarano nell’opzione emits e si emettono con this.$emit('add-to-cart', id). Il genitore li ascolta esattamente allo stesso modo.
remove-from-cart emesso da un secondo pulsante e fai togliere al genitore l'ultimo identificativo dall'array.Errori frequenti
@addtocart e il listener non scatterebbe mai. Usa il kebab-case.