Emits: Komunikasi dari Anak ke Induk
defineEmits, mengirim event dengan payload, mendengarkan event kustom di induk, validasi emit, dan pola "props turun, event naik".
Props turun, event naik
Props hanya membawa data dari induk ke anak. Bagaimana kalau anak ingin memberi tahu induk bahwa "tombol ini diklik" atau "item ini dihapus"? Caranya: anak memancarkan (emit) event kustom, dan induk mendengarkannya dengan @ — persis seperti event DOM.
Pola ini disebut props down, events up dan menjaga aliran data tetap mudah dilacak.
Mendeklarasikan dan memancarkan event
<!-- Anak: Penghitung.vue -->
<script setup>
const emit = defineEmits(['tambah', 'reset'])
</script>
<template>
<button @click="emit('tambah', 1)">+1</button>
<button @click="emit('reset')">Reset</button>
</template>
<!-- Induk -->
<Penghitung @tambah="total += $event" @reset="total = 0" />
<Penghitung @tambah="tambah" /> <!-- method menerima payload -->
function tambah(n) { total.value += n }
emit('nama', ...argumen)— argumen tambahan menjadi payload yang diterima handler.- Di induk, payload pertama tersedia sebagai
$eventdi inline handler, atau sebagai argumen di method handler. - Gunakan nama
kebab-casebila event bernama majemuk:emit('selesai-diubah')→@selesai-diubah(camelCase di emit juga didukung).
Emit di template tanpa variabel
Di template tersedia $emit langsung, tanpa perlu menyimpan hasil defineEmits:
<button @click="$emit('tambah', 1)">+1</button>
Untuk memancarkan dari script, simpan dulu: const emit = defineEmits([...]).
Validasi event (bentuk objek)
const emit = defineEmits({
tambah: (n) => typeof n === 'number', // validator: true = valid
reset: null, // tanpa validasi
})
Contoh nyata: item daftar yang bisa dihapus
<!-- ItemTugas.vue -->
<script setup>
defineProps({ tugas: Object })
const emit = defineEmits(['hapus'])
</script>
<template>
<li>{{ tugas.judul }} <button @click="emit('hapus', tugas.id)">x</button></li>
</template>
<!-- Induk -->
<ItemTugas v-for="t in daftar" :key="t.id" :tugas="t" @hapus="hapusTugas" />
function hapusTugas(id) { daftar.value = daftar.value.filter(t => t.id !== id) }
Perhatikan: anak tidak menghapus data sendiri (ia tak memilikinya). Anak hanya melapor, induk yang memutuskan dan mengubah datanya.
Props vs emits dalam sekali pandang
| Props | Emits | |
|---|---|---|
| Arah | Induk → Anak | Anak → Induk |
| Deklarasi | defineProps | defineEmits |
| Pemakaian induk | :nama="x" | @nama="handler" |
Kesalahan umum
- Memakai event native tanpa mendeklarasikannya: event yang tak dideklarasikan di
defineEmitsdiperlakukan sebagai listener native yang jatuh ke elemen akar. - Mengubah props di anak sebagai "cara cepat" ketimbang emit.
- Lupa bahwa
emittidak naik bertingkat seperti event DOM: hanya induk langsung yang mendengar. Untuk jarak jauh pakai provide/inject atau Pinia.