Tutoriel VueJS 3 – Communication des événements
vérifié le 2 septembre 2026 · 3 min
Un composant enfant signale un changement au parent en émettant un événement : defineEmits le déclare, emit('add-to-cart', id) l’envoie, et le parent l’écoute avec @add-to-cart. Les props descendent, les événements remontent.
Les props descendent du parent vers l’enfant. Pour remonter une information, un composant émet un événement et le parent l’écoute, exactement comme il écouterait un clic.
Le problème
Depuis le découpage du chapitre 8, le panier vit dans App.vue et le bouton dans ProductDisplay.vue. L’enfant ne peut pas modifier cart : il n’y a pas accès, et un composant ne doit pas écrire dans l’état d’un autre.
La bonne formulation n’est pas « l’enfant ajoute au panier », mais « l’enfant annonce qu’on a cliqué, le parent décide quoi en faire ».
Déclarer et émettre
Dans l’enfant, déclarez les événements émis, puis émettez :
<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 est une macro du compilateur, comme defineProps : pas d’import. Elle renvoie la fonction qui sert à émettre.
Déclarer les événements n’est pas obligatoire, mais c’est utile : la liste documente le contrat du composant, l’éditeur la complète automatiquement, et Vue avertit si vous émettez un nom non déclaré.
Écouter côté parent
Un événement personnalisé s’écoute avec la même syntaxe qu’un événement natif :
<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>Cliquez : le compteur repart. Le bouton est dans l’enfant, le compteur dans le parent, et aucun des deux ne connaît l’état de l’autre.
Transmettre une donnée
Un panier réaliste retient ce qu’on y met, pas seulement combien. Passez l’identifiant de la déclinaison en second argument de emit :
<script setup>
function addToCart() {
emit('add-to-cart', variants.value[selectedVariant.value].id)
}
</script>Le parent le reçoit comme paramètre de sa fonction :
<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 est devenu un tableau, l’affichage utilise donc cart.length. Ajoutez plusieurs fois le T-shirt bleu, puis vérifiez dans les outils de développement Vue que le tableau se remplit bien de 2234.
Pour plusieurs informations, émettez un objet plutôt qu’une longue liste d’arguments :
emit('add-to-cart', { id: variant.id, taille: 'M', quantite: 1 })Le nom des événements
Écrivez le nom en kebab-case : add-to-cart, pas addToCart. Les attributs HTML ne distinguent pas les majuscules, un gabarit écrit directement dans une page recevrait @addtocart et l’écouteur ne se déclencherait jamais. Dans un fichier .vue, Vue rattrape la casse, mais autant prendre la bonne habitude.
Un événement personnalisé ne se propage qu’au parent direct. Si trois niveaux de composants séparent l’émetteur du destinataire, il faut le réémettre à chaque étage. Quand cela devient pénible, c’est le signal qu’il faut un état partagé : c’est exactement le rôle de Pinia.
Le sens de circulation
Un avis qui arrive par ce chemin peut ensuite être animé à l’affichage.
Retenez ces deux phrases : les props descendent, les événements remontent. Le parent possède la donnée, l’enfant demande sa modification. C’est ce qui rend le flux prévisible, et l’origine d’un changement toujours traçable.
Les événements se déclarent dans l’option emits et s’émettent avec this.$emit('add-to-cart', id). Le parent les écoute exactement de la même manière.
remove-from-cart émis par un second bouton, et faites retirer le dernier identifiant du tableau par le parent.Erreurs fréquentes
@addtocart et l’écouteur ne se déclencherait jamais. Utilisez le kebab-case.