Props adalah data yang dikirim parent ke child lewat atribut di tag child-nya. Bayangin tempat label harga di etalase Warung Bu Sri. Bentuk tempat labelnya sama semua, tapi Bu Sri menyelipkan kertas yang beda ke tiap tempat: "Nasi Goreng, 15000", "Es Teh, 5000". Tempat label itu cuma menampilkan isi kertasnya. Kalau harganya mau diganti, Bu Sri yang menulis kertas baru, bukan tempat labelnya. Tempat label itu komponen child, kertasnya props, dan Bu Sri parent-nya.
🧪 Tebakan cepat:
MenuCardmenyatakanharga: Numberdan menampilkanDibungkus: Rp{{ harga + 2000 }}. Parent menulis<MenuCard nama="Nasi Goreng" harga="15000" />, tanpa titik dua di depanharga. Apa yang tampil? Tebak dulu, jawabannya ada di bagian "Titik dua: teks atau nilai JavaScript".
Parent mengirim, child menerima
Child menyatakan props apa saja yang dia terima dengan defineProps:
<!-- MenuCard.vue -->
<script setup>
const props = defineProps({
nama: String,
harga: Number,
stok: Number,
})
</script>
<template>
<div class="kartu">
<h3>{{ nama }}</h3>
<p>Rp{{ harga }}</p>
<small>Stok: {{ stok }}</small>
</div>
</template>
Parent mengisinya lewat atribut, satu tag satu salinan:
<!-- App.vue -->
<script setup>
import MenuCard from "./MenuCard.vue"
</script>
<template>
<MenuCard nama="Nasi Goreng" :harga="15000" :stok="8" />
<MenuCard nama="Mie Ayam" :harga="12000" :stok="0" />
<MenuCard nama="Es Teh" :harga="5000" :stok="20" />
</template>
| Tag di App.vue | Yang tampil |
|---|---|
nama="Nasi Goreng" :harga="15000" :stok="8" |
Nasi Goreng, Rp15000, Stok: 8 |
nama="Mie Ayam" :harga="12000" :stok="0" |
Mie Ayam, Rp12000, Stok: 0 |
nama="Es Teh" :harga="5000" :stok="20" |
Es Teh, Rp5000, Stok: 20 |
definePropsadalah compiler macro: Vue sendiri yang mengenalinya di<script setup>, jadi gak perlu di-import.- Di template, props dibaca langsung:
{{ nama }}. Di script, lewat objek yang dikembalikan:props.nama. - Satu komponen, tiga salinan, data beda. Itu gunanya props.
Titik dua: teks atau nilai JavaScript
nama="Nasi Goreng"tanpa titik dua mengirim teks apa adanya.:harga="15000"adalah singkatanv-bind:harga. Isinya dijalankan sebagai JavaScript, jadi child menerima angka 15000. Nama ref juga boleh::stok="stokNasi".harga="15000"tanpa titik dua mengirim teks"15000", walau bentuknya angka.
Teks ditambah angka di JavaScript berarti disambung, bukan dijumlah. "15000" + 2000 jadi "150002000", jadi kartunya menulis Dibungkus: Rp150002000. Itu jawaban tebakannya. Dengan :harga="15000", hasilnya Dibungkus: Rp17000.
Build development memberi tahu kesalahannya: [Vue warn]: Invalid prop: type check failed for prop "harga". Expected Number with value 15000, got String with value "15000". Editor latihan memakai build production, jadi warning seperti ini gak muncul di sana. Angka yang aneh itu satu-satunya petunjuk.
Tipe, required, dan default
Tiap prop bisa diberi aturan:
const props = defineProps({
nama: { type: String, required: true },
harga: { type: Number, required: true },
stok: { type: Number, default: 0 },
catatan: { type: Array, default: () => [] },
})
typeadalah tipe yang diharapkan:String,Number,Boolean,Array,Object,Function.required: trueartinya parent wajib mengirimnya. Kalau lupa, kartunya tetap dirender dengannamakosong, dan build development menulis[Vue warn]: Missing required prop: "nama".defaultdipakai kalau parent gak mengirim apa-apa.<MenuCard nama="Es Teh" :harga="5000" />menampilkanStok: 0.- Default berupa array atau objek ditulis sebagai fungsi,
() => [], supaya tiap salinan dapat array baru.default: []membuat semua salinan berbagi satu array yang sama.
Props cuma bisa dibaca
Data mengalir satu arah: dari parent ke child. Child boleh membaca props, gak boleh mengubahnya.
<!-- MenuCard.vue, cara yang salah -->
<button @click="stok--">Beli 1</button>
- Klik tombol itu gak mengubah apa-apa. Build development menulis
[Vue warn]: Attempting to mutate prop "stok". Props are readonly.Di script,props.stok--ditolak dengan[Vue warn] Set operation on key "stok" failed: target is readonly. - Stok milik parent, jadi parent yang mengubahnya. Begitu ref
stokdi parent berubah, angka baru ikut turun ke child. - Child yang perlu memberi tahu parent "ada yang beli" mengirim event. Itu pelajaran berikutnya, emits.
- Kalau child cuma butuh nilai awal lalu mengurusnya sendiri, salin ke ref miliknya:
const sisa = ref(props.stok).
📌 Nama prop dua kata
Di script, nama prop ditulis camelCase: hargaPromo. Di template parent, biasanya ditulis kebab-case: <MenuCard :harga-promo="13000" />. Vue menganggap harga-promo dan hargaPromo prop yang sama, jadi keduanya jalan.
File .vue dan editor latihan
Editor latihan menjalankan satu komponen: script, garis ---template---, lalu template. Child ditulis sebagai objek di script, dan dipasang dengan <component :is>:
const MenuCard = {
props: {
nama: String,
harga: Number,
stok: Number,
},
template: `
<div class="kartu">
<h3>{{ nama }}</h3>
<p>Rp{{ harga }}</p>
<small>Stok: {{ stok }}</small>
</div>
`,
}
---template---
<div>
<component :is="MenuCard" nama="Nasi Goreng" :harga="15000" :stok="8" />
<component :is="MenuCard" nama="Es Teh" :harga="5000" :stok="20" />
</div>
| Di file .vue | Di editor latihan |
|---|---|
defineProps({ ... }) di MenuCard.vue |
opsi props: { ... } di objek MenuCard |
<template> milik MenuCard.vue |
opsi template berupa teks di antara backtick |
import MenuCard from "./MenuCard.vue" lalu <MenuCard ... /> |
<component :is="MenuCard" ... /> |
- Editor gak bisa mendaftarkan nama komponen, jadi
<MenuCard />di sana cuma jadi tag kosong<menucard>.<component :is="MenuCard">adalah fitur Vue untuk merender komponen dari sebuah variabel. definePropscuma berlaku di<script setup>file.vue. Di editor dia gak menerima apa-apa.- Latihan pelajaran ini memakai bentuk itu:
ProdukCarddanProdukItemmenyatakanprops, dan parent mengirim nilainya dengan:title="title"atau:nama="p.nama". Pemeriksanya membaca HTML hasilnya, opsipropsdi child, dan atribut yang dikirim parent.
⚠️ Jebakan yang sering ketemu
- Lupa titik dua untuk angka.
harga="15000"mengirim teks. Pakai:harga="15000". - Mengubah prop di child. Gak ada yang berubah. Ubah data di parent, atau kirim event ke parent.
- Default array atau objek tanpa fungsi. Tulis
default: () => [], bukandefault: []. - Mengetik ulang nilainya di template. Latihan memeriksa bahwa
<h2 id="title">diisi{{ title }}dan<span id="price">diisi{{ price }}di templateProdukCard, bukan teksLaptopyang diketik langsung.
TL;DR: Props adalah data dari parent ke child. Child menyatakannya dengan defineProps({ nama: String, harga: Number }), parent mengirimnya lewat atribut: <MenuCard nama="Nasi Goreng" :harga="15000" />. Tanpa titik dua yang dikirim teks, dengan titik dua nilai JavaScript. type, required, dan default mengatur aturannya, dan default array atau objek ditulis sebagai fungsi. Props cuma bisa dibaca: data mengalir satu arah, dan perubahannya dilakukan parent. Di editor latihan, child ditulis sebagai objek dengan opsi props dan template, lalu dipasang dengan <component :is="MenuCard">.