Capítulo 5 de 11

Vue 3 v-on: gerir eventos com @click, $event e modificadores

verificado a 7 Setembro 2026 · 6 min

Resposta rápida

v-on:click, ou o seu atalho @click, executa código quando o evento acontece. Passa o nome de uma função para receberes o evento nativo, ou chama-a com os seus argumentos e o $event se precisares dos dois. Os modificadores .prevent, .stop, .once, .enter ou .esc dispensam o código repetitivo de gestão de eventos.

No fim deste capítulo, vais saber reagir aos cliques, à passagem do rato e às teclas do teclado, passar argumentos aos teus gestores, obter o evento nativo e usar os modificadores para escrever menos código.

Por agora, a aplicação mostra. Não reage a nada. A diretiva v-on liga um listener de evento a um elemento e executa código quando esse evento acontece. Este capítulo cobre tudo o que se faz com ela no dia a dia: um clique, a passagem do rato, uma tecla, com ou sem argumento, e os modificadores que evitam escrever preventDefault() à mão.

O carrinho

Acrescenta um contador e um botão:

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

Escutar um clique

v-on: seguido do nome do evento, com o código a executar entre aspas:

src/App.vue
<button class="button" v-on:click="cart += 1">Ajouter au panier</button>

Clica: o contador sobe. O nome do evento é o do DOM padrão, sem prefixo: click, submit, input, keyup, mouseover.

Chamar uma função

Uma expressão em linha serve para uma operação trivial. Assim que a lógica passa de uma linha, escreve uma função:

src/App.vue
<script setup>
function addToCart() {
  cart.value += 1
}
</script>

<template>
  <button class="button" v-on:click="addToCart">Ajouter au panier</button>
</template>

Repara no .value dentro do script: estamos a manipular a referência, não o valor desembrulhado. No template, escreve-se sempre {{ cart }} sem .value.

Com ou sem parênteses?

@click="addToCart" passa a própria função: o Vue chama-a com o evento nativo como primeiro argumento. @click="addToCart()" chama-a sem argumento. Aqui, ambas funcionam. O erro clássico surge quando a função espera um valor: @click="updateImage(variant.image)" está correto, mas esquecer os parênteses passaria o evento em vez do caminho da imagem.

O atalho arroba

Tal como o v-bind tem o :, o v-on tem o @:

src/App.vue
<button class="button" @click="addToCart">Ajouter au panier</button>

É esta a escrita que vais encontrar em toda a documentação e no resto deste tutorial.

Passar um argumento

Vamos fazer a imagem mudar quando o rato passa sobre uma cor. A função recebe o caminho da imagem da variante em causa:

src/App.vue
<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 o rato sobre «Rouge»: a imagem fica vermelha. Combinam-se aqui dois mecanismos, o ciclo do capítulo 4 e a ligação de atributo do capítulo 2.

Obter o evento nativo

Sem argumento, a função recebe o evento do DOM. Com argumentos, pede-o explicitamente com $event:

html
<button @click="voir">Sans argument</button>
<button @click="voirAvec('bleu', $event)">Avec arguments</button>
javascript
function voir(event) {
  console.log(event.target)
}

function voirAvec(couleur, event) {
  console.log(couleur, event.target)
}

O objeto recebido é o evento nativo do navegador, sem invólucro: event.target, event.key, event.preventDefault() funcionam como em JavaScript puro.

Os modificadores de evento

O Vue acrescenta sufixos que dispensam o código repetitivo de gestão de eventos:

html
<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 -->

A lista completa, com aquilo que cada modificador substitui:

Modificador Equivalente em JavaScript Uso típico
.prevent event.preventDefault() um formulário que não deve recarregar a página
.stop event.stopPropagation() um botão dentro de um cartão clicável: o clique não sobe até ao cartão
.self if (event.target !== event.currentTarget) return fechar uma janela modal com um clique no fundo, não no seu conteúdo
.once addEventListener(…, { once: true }) um botão «Encomendar» que não deve enviar duas vezes
.capture addEventListener(…, { capture: true }) intercetar o evento antes dos filhos
.passive addEventListener(…, { passive: true }) deslocamento e toque no telemóvel, para não bloquear a renderização

Os modificadores encadeiam-se e leem-se da esquerda para a direita: @click.stop.prevent pára a propagação e só depois anula o comportamento por omissão. Esta ordem conta com o .self: @click.self.prevent só anula o comportamento por omissão dos cliques no próprio elemento, @click.prevent.self anula-o para todos os cliques, filhos incluídos.

O @submit.prevent vai servir no capítulo 10 para impedir o recarregamento da página no envio do formulário.

Modificadores de tecla e de rato

Nos eventos de teclado, um modificador filtra a tecla. O Vue aceita qualquer nome de tecla exposto por KeyboardEvent.key, escrito em kebab-case, mais alguns alias:

html
<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 -->

Os alias disponíveis: .enter, .tab, .delete (Delete e Backspace), .esc, .space, .up, .down, .left, .right. As teclas de sistema .ctrl, .alt, .shift e .meta combinam-se com as outras. E o .exact exige que nenhuma outra tecla de sistema esteja premida:

html
<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 -->

Para o rato, .left, .right e .middle filtram o botão: @click.right.prevent="menuContextuel" substitui o menu do navegador pelo teu.

Vários gestores no mesmo evento

Um evento pode desencadear várias funções, separadas por vírgulas. Nesse caso, cada função é chamada com os parênteses:

html
<button @click="addToCart(), trackClick('ajout-panier')">Ajouter au panier</button>

Para lá de duas chamadas, deixa de ser legível: escreve uma função que chame as outras.

Em resumo

  • v-on:click ou @click: uma expressão em linha para uma operação trivial, caso contrário uma função.
  • Sem parênteses, a função recebe o evento nativo; com argumentos, o $event passa-o também.
  • .prevent, .stop, .self, .once substituem o código repetitivo; os modificadores de tecla filtram o teclado; o .exact tranca as combinações.
  • No script, o estado altera-se com .value; no template, sem ele.
Na Options API

As funções vivem na opção methods e chegam ao estado através do this: methods: { addToCart() { this.cart += 1 } }. O template, esse, não muda.

Os eventos deste capítulo vêm do DOM. Para que um componente filho avise o seu pai, o Vue tem eventos personalizados com emit: é o tema do capítulo 9, detalhado no guia completo do Vue emit.

ExercícioAcrescenta um botão «Retirar do carrinho» que decremente o contador sem nunca descer abaixo de zero. Depois substitui a passagem do rato por um clique nas cores e compara a sensação.

Erros frequentes

Esquecer o .value dentro de uma função No script, cart += 1 não faz nada de útil numa referência: escreve cart.value += 1.
Parênteses em falta com um argumento @click="updateImage" passa o evento nativo em vez do valor esperado. Com um argumento, chama a função: @click="updateImage(variant.image)".
Obter o evento quando já se passa um argumento É preciso pedi-lo explicitamente com $event: @click="faire('bleu', $event)".
Encadear os modificadores pela ordem errada @click.prevent.self impede o comportamento por omissão de todos os cliques, @click.self.prevent apenas o daqueles cujo alvo é o próprio elemento. A ordem lê-se da esquerda para a direita.
Usar .passive com .prevent O .passive promete ao navegador que não vai chamar preventDefault(); combinado com .prevent, o navegador ignora um dos dois e mostra um aviso.
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.