Vue 3: gestire gli eventi con v-on (@click, $event, modificatori)
verificato il 7 Settembre 2026 · 6 min
v-on:click, o la sua scorciatoia @click, esegue del codice quando l'evento si verifica. Passa il nome di una funzione per ricevere l'evento nativo, oppure chiamala con i suoi argomenti e $event se ti servono entrambi. I modificatori .prevent, .stop, .once, .enter o .esc sostituiscono il codice ripetitivo di gestione degli eventi.
Per ora l’applicazione mostra e basta. Non reagisce a niente. La direttiva v-on collega un listener di eventi a un elemento ed esegue del codice quando l’evento si verifica. Questo capitolo copre tutto quello che se ne fa ogni giorno: un clic, il passaggio del mouse, un tasto, con o senza argomenti, e i modificatori che evitano di scrivere preventDefault() a mano.
Il carrello
Aggiungi un contatore e un pulsante:
<script setup>
import { ref } from 'vue'
const product = ref('T-Shirt')
const image = ref('/images/t-shirt-bleu.svg')
const inventory = ref(100)
const cart = ref(0)
const details = ref(['60 % coton', '30 % laine', '10 % polyester'])
const variants = ref([
{ id: 2234, color: 'Bleu', image: '/images/t-shirt-bleu.svg' },
{ id: 2235, color: 'Rouge', image: '/images/t-shirt-rouge.svg' }
])
</script>
<template>
<div class="product-display">
<div class="cart">Panier ({{ cart }})</div>
…
<button class="button">Ajouter au panier</button>
</div>
</template>Ascoltare un clic
v-on: seguito dal nome dell’evento, con il codice da eseguire tra virgolette:
<button class="button" v-on:click="cart += 1">Ajouter au panier</button>Clicca: il contatore sale. Il nome dell’evento è quello standard del DOM, senza prefisso: click, submit, input, keyup, mouseover.
Chiamare una funzione
Un’espressione inline va bene per un’operazione banale. Appena la logica supera una riga, scrivi una funzione:
<script setup>
function addToCart() {
cart.value += 1
}
</script>
<template>
<button class="button" v-on:click="addToCart">Ajouter au panier</button>
</template>Nota il .value nello script: stiamo manipolando la ref, non il valore già estratto. Nel template si scrive sempre {{ cart }}, senza .value.
@click="addToCart" passa la funzione stessa: Vue la chiama con l’evento nativo come primo argomento. @click="addToCart()" la chiama senza argomenti. Qui funzionano entrambe. L’errore classico arriva quando la funzione si aspetta un valore: @click="updateImage(variant.image)" è corretto, ma dimenticare le parentesi passerebbe l’evento al posto del percorso dell’immagine.
La scorciatoia con la chiocciola
Come v-bind ha :, v-on ha @:
<button class="button" @click="addToCart">Ajouter au panier</button>È la scrittura che vedrai in tutta la documentazione e nel resto di questo tutorial.
Passare un argomento
Facciamo cambiare l’immagine al passaggio del mouse su un colore. La funzione riceve il percorso dell’immagine della variante sotto il cursore:
<script setup>
function updateImage(variantImage) {
image.value = variantImage
}
</script>
<template>
<div
v-for="variant in variants"
:key="variant.id"
@mouseover="updateImage(variant.image)"
>
{{ variant.color }}
</div>
</template>Passa il mouse su «Rouge»: l’immagine diventa rossa. Qui si combinano due meccanismi, il ciclo del capitolo 4 e il binding di attributo del capitolo 2.
Recuperare l’evento nativo
Senza argomenti, la funzione riceve l’evento del DOM. Con degli argomenti, chiedilo esplicitamente con $event:
<button @click="voir">Sans argument</button>
<button @click="voirAvec('bleu', $event)">Avec arguments</button>function voir(event) {
console.log(event.target)
}
function voirAvec(couleur, event) {
console.log(couleur, event.target)
}L’oggetto ricevuto è l’evento nativo del browser, senza involucro: event.target, event.key, event.preventDefault() funzionano come in JavaScript puro.
I modificatori di evento
Vue aggiunge dei suffissi che evitano il codice ripetitivo di gestione degli eventi:
<form @submit.prevent="envoyer">…</form> <!-- event.preventDefault() -->
<div @click.stop="…">…</div> <!-- event.stopPropagation() -->
<div @click.self="…">…</div> <!-- seulement si la cible est cet élément -->
<button @click.once="…">…</button> <!-- une seule fois -->
<input @keyup.enter="valider"> <!-- seulement la touche Entrée -->La lista completa, con quello che ogni modificatore sostituisce:
| Modificatore | Equivalente JavaScript | Uso tipico |
|---|---|---|
.prevent | event.preventDefault() | un form che non deve ricaricare la pagina |
.stop | event.stopPropagation() | un pulsante dentro una card cliccabile: il clic non risale alla card |
.self | if (event.target !== event.currentTarget) return | chiudere una finestra modale al clic sullo sfondo, non sul suo contenuto |
.once | addEventListener(…, { once: true }) | un pulsante «Ordina» che non deve inviare due volte |
.capture | addEventListener(…, { capture: true }) | intercettare l’evento prima dei figli |
.passive | addEventListener(…, { passive: true }) | scorrimento e tocco su mobile, per non bloccare il rendering |
I modificatori si concatenano e si leggono da sinistra a destra: @click.stop.prevent ferma la propagazione, poi annulla il comportamento predefinito. Quest’ordine conta con .self: @click.self.prevent annulla il comportamento predefinito solo per i clic sull’elemento stesso, @click.prevent.self lo annulla per tutti i clic, figli compresi.
@submit.prevent servirà al capitolo 10 per impedire il ricaricamento della pagina all’invio del form.
Modificatori di tasto e di mouse
Sugli eventi da tastiera, un modificatore filtra il tasto. Vue accetta qualsiasi nome di tasto esposto da KeyboardEvent.key, scritto in kebab-case, più qualche alias:
<input @keyup.enter="valider">
<input @keyup.esc="annuler">
<input @keydown.tab="suivant">
<input @keyup.page-down="pageSuivante"> <!-- KeyboardEvent.key === 'PageDown' -->
<input @keydown.ctrl.enter="envoyer"> <!-- Ctrl + Entrée -->Gli alias disponibili: .enter, .tab, .delete (Canc e Backspace), .esc, .space, .up, .down, .left, .right. I tasti di sistema .ctrl, .alt, .shift e .meta si combinano con gli altri. E .exact pretende che nessun altro tasto di sistema sia premuto:
<button @click.ctrl="ouvrir">…</button> <!-- Ctrl + clic, même si Shift est aussi enfoncé -->
<button @click.ctrl.exact="ouvrir">…</button> <!-- Ctrl + clic, et rien d'autre -->
<button @click.exact="ouvrir">…</button> <!-- clic sans aucune touche système -->Per il mouse, .left, .right e .middle filtrano il pulsante: @click.right.prevent="menuContextuel" sostituisce il menu del browser con il tuo.
Più gestori sullo stesso evento
Un evento può far partire più funzioni, separate da virgole. Ogni funzione viene allora chiamata con le parentesi:
<button @click="addToCart(), trackClick('ajout-panier')">Ajouter au panier</button>Oltre le due chiamate non è più leggibile: scrivi una funzione che chiama le altre.
Riepilogo
v-on:clicko@click: un’espressione inline per un’operazione banale, altrimenti una funzione.- Senza parentesi, la funzione riceve l’evento nativo; con degli argomenti,
$eventlo passa in più. .prevent,.stop,.self,.oncesostituiscono il codice ripetitivo; i modificatori di tasto filtrano la tastiera;.exactblocca le combinazioni.- Nello script lo stato si modifica con
.value; nel template, senza.
Le funzioni vivono nell’opzione methods e accedono allo stato con this: methods: { addToCart() { this.cart += 1 } }. Il template, invece, non cambia.
Gli eventi di questo capitolo vengono dal DOM. Perché un componente figlio avvisi il suo genitore, Vue propone gli eventi personalizzati con emit: è l’argomento del capitolo 9, approfondito nella guida completa a Vue emit.
Errori frequenti
cart += 1 non fa niente di utile su una ref: scrivi cart.value += 1.@click="updateImage" passa l'evento nativo al posto del valore atteso. Con un argomento, chiama la funzione: @click="updateImage(variant.image)".$event: @click="faire('bleu', $event)".@click.prevent.self impedisce il comportamento predefinito di tutti i clic, @click.self.prevent solo di quelli il cui bersaglio è l'elemento stesso. L'ordine si legge da sinistra a destra..passive promette al browser di non chiamare preventDefault(); combinato con .prevent, il browser ne ignora uno dei due e mostra un avviso.