Episode ini membahas komunikasi antar komponen di Vue: props untuk data masuk, defineEmits untuk event keluar, provide/inject untuk dependency injection, slots dan scoped slots, serta pattern komponen yang dapat dipakai ulang.

Satu komponen jarang berdiri sendiri. Aplikasi nyata adalah jaringan komponen yang saling bertukar data dan perintah. Kemampuan mendesain antarmuka komunikasi yang bersih — siapa mengirim apa, siapa mendengar apa — adalah salah satu ciri developer Vue yang matang.
Episode 6 membedah empat kanal komunikasi di Vue: props untuk data masuk, emits untuk event keluar, provide/inject untuk melewati pohon komponen, dan slots untuk komposisi konten. Terakhir kita bahas pattern komponen reusable yang menjaga UI tetap konsisten dan mudah dipelihara.
Props adalah cara utama komponen menerima data dari parent:
<script setup>
const props = defineProps({
judul: { type: String, required: true },
jumlah: { type: Number, default: 0 },
});
</script>
<template>
<h2>{{ props.judul }}</h2>
<span>{{ props.jumlah }}</span>
</template>defineProps di sisi child mendeklarasikan kontrak data. Parent cukup mengoper seperti atribut HTML:
<template>
<ProductBadge judul="Diskon" :jumlah="3" />
</template>judul="Diskon" mengirim string literal, sedangkan :jumlah="3" mengirim nilai reactive. Props bersifat satu arah: child tidak boleh mengubah props, melainkan memicu event jika perlu mengubah data di parent.
Untuk memberi tahu parent, child mengirim custom event:
<script setup>
const emit = defineEmits(["simpan", "batal"]);
function simpanData() {
emit("simpan", { id: 1 });
}
</script>
<template>
<button @click="simpanData">Simpan</button>
</template>defineEmits(["simpan", "batal"]) mendeklarasikan event yang bisa dikirim. Parent mendengarkannya seperti event biasa:
<script setup>
import ProductForm from "./ProductForm.vue";
function onSimpan(data) {
console.log("data diterima:", data);
}
</script>
<template>
<ProductForm @simpan="onSimpan" />
</template>@simpan="onSimpan" menjalankan handler saat child memanggil emit("simpan", ...). Ini membentuk pola data-down, events-up yang membuat alur data mudah dilacak.
Saat banyak lapisan komponen membutuhkan data yang sama, mengoper lewat props satu per satu menjadi membosankan dan rapuh. provide di ancestor dan inject di descendant memotong rantai:
import { provide, ref } from "vue";
const tema = ref("dark");
provide("tema", tema);import { inject } from "vue";
const tema = inject("tema");inject("tema") mengambil nilai yang di-provide dengan key "tema" dari ancestor terdekat. Karena key bersifat string, pastikan penamaannya konsisten agar tidak tabrakan.
Provide/inject berguna untuk konfigurasi global seperti tema, locale, atau session user. Batasannya: hubungan ini tidak terlihat eksplisit di tree komponen, jadi gunakan untuk kebutuhan yang memang bersifat global, bukan untuk semua komunikasi — komunikasi antar komponen terdekat tetap lebih baik memakai props dan emits.
Slots memungkinkan parent mengisi konten ke dalam child:
<template>
<article class="card">
<header><slot name="judul" /></header>
<div><slot /></div>
</article>
</template>Slot bernama judul dan slot default menampung konten dari parent:
<template>
<Card>
<template #judul>Produk Unggulan</template>
<p>Isi konten utama card.</p>
</Card>
</template><template #judul> mengisi slot bernama, dan teks di dalam <Card> mengisi slot default. Slot membuat komponen menjadi fleksibel tanpa menambah props.
Scoped slot mengirim data child lewat atribut slot, lalu parent menerimanya dengan v-slot:
<template>
<ItemList>
<template #default="{ item }">
<b>Item: {{ item }}</b>
</template>
</ItemList>
</template>#default="{ item }" mengakses data yang dikirim child — child mengontrol struktur, parent mengontrol tampilan.
Untuk konsistensi, kumpulkan elemen UI dasar sebagai base component dengan nama seragam:
src/components/base/BaseButton.vue
src/components/base/BaseInput.vue
src/components/base/BaseModal.vueBase component umumnya: menyeragamkan gaya, menerima props untuk variasi, dan menyebarkan atribut ke elemen DOM.
Aturan praktis: gunakan props untuk data dan perilaku terstruktur, slots untuk konten bebas yang tidak bisa diprediksi, dan scoped slots ketika konten membutuhkan data internal komponen. Kombinasi ketiganya menghasilkan komponen yang reusable tanpa menjadi rumit.
Episode 6 membekali kalian empat kanal komunikasi Vue: props untuk data masuk, emits untuk event keluar, provide/inject untuk nilai global di tree komponen, serta slots dan scoped slots untuk komposisi konten yang fleksibel.
Inti yang harus dibawa pulang:
defineEmits mendeklarasikan event keluar dari child.#nama untuk konten fleksibel.Di episode 7 selanjutnya kita membahas directives dan custom directives — v-model untuk two-way binding, v-show, v-cloak, dan v-pre, modifier untuk v-model, serta cara membuat custom directives dengan lifecycle hooks untuk kebutuhan spesifik.