Capitolo 9 di 11

Tutorial VueJS 3: comunicare gli eventi con defineEmits

verificato il 2 Settembre 2026 · 3 min

Risposta rapida

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.

Alla fine di questo capitolo, saprai far risalire un'informazione da un componente al suo genitore.

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:

src/components/ProductDisplay.vue
<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:

src/App.vue
<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:

src/components/ProductDisplay.vue
<script setup>
function addToCart() {
  emit('add-to-cart', variants.value[selectedVariant.value].id)
}
</script>

Il genitore lo riceve come parametro della sua funzione:

src/App.vue
<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:

javascript
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.

Gli eventi non risalgono da soli

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.

Con la Options API

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.

EsercizioAggiungi un evento remove-from-cart emesso da un secondo pulsante e fai togliere al genitore l'ultimo identificativo dall'array.

Errori frequenti

Chiamare l'evento in camelCase Gli attributi HTML ignorano le maiuscole: un template fuori da un file .vue riceverebbe @addtocart e il listener non scatterebbe mai. Usa il kebab-case.
Aspettarsi che un evento attraversi più livelli Un evento arriva solo al genitore diretto. Oltre i due livelli, passa a uno stato condiviso con Pinia.
Modificare lo stato del genitore dal figlio Il figlio segnala, il genitore decide. È questo che mantiene tracciabile l'origine dei cambiamenti.
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.