Kapitel 9 von 11

VueJS-3-Tutorial – Kommunikation mit Events und defineEmits

geprüft am 2 September 2026 · 3 Min.

Schnelle Antwort

Eine Kindkomponente meldet der Elternkomponente eine Änderung, indem sie ein Event emittiert: defineEmits deklariert es, emit('add-to-cart', id) sendet es, und die Elternkomponente hört es mit @add-to-cart ab. Props gehen nach unten, Events nach oben.

Am Ende dieses Kapitels, kannst du eine Information von einer Komponente zu ihrer Elternkomponente hochreichen.

Props gehen von der Eltern- zur Kindkomponente hinunter. Um eine Information nach oben zu geben, emittiert eine Komponente ein Event, und die Elternkomponente hört darauf, genau wie sie auf einen Klick hören würde.

Das Problem

Seit der Aufteilung aus Kapitel 8 lebt der Warenkorb in App.vue und der Button in ProductDisplay.vue. Das Kind kann cart nicht ändern: es hat keinen Zugriff darauf, und eine Komponente darf nicht in den Zustand einer anderen schreiben.

Die richtige Formulierung lautet nicht „das Kind legt in den Warenkorb“, sondern „das Kind meldet einen Klick, die Elternkomponente entscheidet, was daraus wird“.

Deklarieren und emittieren

Deklarier in der Kindkomponente die Events, die sie aussendet, und emittier dann:

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 ist wie defineProps ein Compiler-Makro: kein Import nötig. Es liefert die Funktion zurück, mit der emittiert wird.

Die Events zu deklarieren ist nicht Pflicht, aber sinnvoll: die Liste dokumentiert den Vertrag der Komponente, der Editor vervollständigt sie automatisch, und Vue warnt, wenn du einen nicht deklarierten Namen emittierst.

In der Elternkomponente zuhören

Ein eigenes Event hörst du mit derselben Syntax ab wie ein natives:

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>

Klick: der Zähler läuft wieder. Der Button steckt im Kind, der Zähler in der Elternkomponente, und keiner von beiden kennt den Zustand des anderen.

Daten mitgeben

Ein realistischer Warenkorb merkt sich, was hineinkommt, nicht nur wie viel. Gib die ID der Variante als zweites Argument an emit:

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

Die Elternkomponente bekommt sie als Parameter ihrer Funktion:

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 ist jetzt ein Array, die Anzeige nutzt deshalb cart.length. Leg das blaue T-Shirt mehrmals hinein und prüf in den Vue-Devtools, dass sich das Array tatsächlich mit 2234 füllt.

Für mehrere Informationen emittier lieber ein Objekt als eine lange Argumentliste:

javascript
emit('add-to-cart', { id: variant.id, taille: 'M', quantite: 1 })

Die Namen der Events

Schreib den Namen in kebab-case: add-to-cart, nicht addToCart. HTML-Attribute unterscheiden keine Groß- und Kleinschreibung, ein direkt in einer Seite geschriebenes Template bekäme @addtocart, und der Listener würde nie auslösen. In einer .vue-Datei fängt Vue die Schreibweise ab, aber gewöhn es dir gleich richtig an.

Events steigen nicht von allein nach oben

Ein eigenes Event erreicht nur die direkte Elternkomponente. Liegen drei Komponentenebenen zwischen Sender und Empfänger, musst du es auf jeder Etage erneut emittieren. Sobald das lästig wird, ist es das Zeichen für einen geteilten Zustand: genau das ist die Aufgabe von Pinia.

Die Fließrichtung

Eine Bewertung, die auf diesem Weg ankommt, lässt sich anschließend beim Einblenden animieren.

Merk dir diese zwei Sätze: Props gehen nach unten, Events gehen nach oben. Die Elternkomponente besitzt die Daten, das Kind bittet um ihre Änderung. Das macht den Fluss vorhersehbar und den Ursprung einer Änderung jederzeit nachvollziehbar.

In der Options API

Events werden in der Option emits deklariert und mit this.$emit('add-to-cart', id) emittiert. Die Elternkomponente hört sie genau gleich ab.

ÜbungErgänze ein Event remove-from-cart, das ein zweiter Button emittiert, und lass die Elternkomponente die letzte ID aus dem Array entfernen.

Häufige Fehler

Das Event in camelCase benennen HTML-Attribute ignorieren die Schreibweise: ein Template außerhalb einer .vue-Datei bekäme @addtocart, und der Listener würde nie auslösen. Nimm kebab-case.
Erwarten, dass ein Event mehrere Ebenen durchquert Ein Event geht nur zur direkten Elternkomponente. Ab zwei Ebenen wechsel zu einem geteilten Zustand mit Pinia.
Den Zustand der Elternkomponente vom Kind aus ändern Das Kind meldet, die Elternkomponente entscheidet. Genau das hält den Ursprung der Änderungen nachvollziehbar.
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.