Chapitre 9 sur 11

Tutoriel VueJS 3 – Communication des événements

vérifié le 2 septembre 2026 · 3 min

Réponse rapide

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.

À la fin de ce chapitre, vous saurez faire remonter une information d’un composant vers son parent.

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 :

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

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>

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 :

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

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

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

Les événements ne remontent pas tout seuls

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.

En Options API

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.

ExerciceAjoutez un événement remove-from-cart émis par un second bouton, et faites retirer le dernier identifiant du tableau par le parent.

Erreurs fréquentes

Nommer l’événement en camelCase Les attributs HTML ignorent la casse : un gabarit hors fichier .vue recevrait @addtocart et l’écouteur ne se déclencherait jamais. Utilisez le kebab-case.
Attendre qu’un événement traverse plusieurs niveaux Un événement ne va qu’au parent direct. Au-delà de deux niveaux, passez à un état partagé avec Pinia.
Modifier l’état du parent depuis l’enfant L’enfant signale, le parent décide. C’est ce qui garde l’origine des changements traçable.
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.