v-for mengulang satu elemen untuk setiap item di sebuah array. Bayangin Bu Sri bikin kartu menu. Dia gak menulis tiap kartu dengan tangan. Dia punya satu cetakan kartu, lalu mencapnya sekali untuk setiap baris di buku catatan menunya. Tambah satu baris di buku, muncul satu kartu baru. Di Vue, cetakan itu elemen yang diberi v-for, buku catatannya array. Supaya Bu Sri tahu kartu mana yang harus dicopot saat satu menu habis, tiap kartu diberi label nama: itu :key.
🧪 Tebakan cepat: Tiap baris menu punya input catatan, dan
:key-nya nomor urut baris. Kamu mengetik "pedas" di baris Nasi Goreng, lalu Nasi Goreng dihapus karena habis. Sekarang "pedas" ada di baris mana? Tebak dulu, jawabannya ada di bagian "Kenapa :key penting".
v-for: satu elemen per item
const menu = ref([
{ id: 1, nama: "Nasi Goreng", harga: 15000 },
{ id: 2, nama: "Mie Ayam", harga: 12000 },
{ id: 3, nama: "Es Teh", harga: 5000 },
])
---template---
<ul>
<li v-for="m in menu" :key="m.id">{{ m.nama }}: Rp{{ m.harga }}</li>
</ul>
Hasil render-nya:
<ul>
<li>Nasi Goreng: Rp15000</li>
<li>Mie Ayam: Rp12000</li>
<li>Es Teh: Rp5000</li>
</ul>
v-for="m in menu"artinya "untuk setiap item dimenu, sebut item itum". Namanya bebas, danmcuma bisa dipakai di dalam elemen itu.- Directive-nya ditaruh di elemen yang diulang, yaitu
<li>, bukan di pembungkusnya<ul>. :key="m.id"memberi tiap<li>tanda pengenal unik. Alasannya ada di bagian ":key" di bawah.- Array-nya reaktif.
menu.value.push({ id: 4, nama: "Teh Manis", harga: 4000 })di script langsung menambah satu<li>. - Latihan pertama memakai array teks:
const buah = ref(["Apel", "Mangga", "Jeruk"])dan<li v-for="item in buah" :key="item">{{ item }}</li>. Teksnya sendiri unik, jadi bisa jadi key. Isi<li>cuma{{ item }}: latihan menolak<li>yang isinya diketik langsung.
Nomor urut: (item, index)
Dengan menu yang sama di script:
---template---
<ul>
<li v-for="(m, i) in menu" :key="m.id">{{ i + 1 }}. {{ m.nama }}</li>
</ul>
i |
Yang tampil |
|---|---|
| 0 | 1. Nasi Goreng |
| 1 | 2. Mie Ayam |
| 2 | 3. Es Teh |
- Nama kedua di dalam kurung adalah index, posisi item, dan mulai dari 0. Karena itu nomornya ditulis
i + 1. - Item yang berupa object dibaca propertinya. Latihan kedua:
siswaberisi{ nama: "Ani" },{ nama: "Budi" },{ nama: "Cici" }, dan tiap butirnya<li v-for="(s, index) in siswa" :key="index">{{ s.nama }}</li>di dalam<ol id="daftar">.<ol>sudah memberi nomor sendiri, jadiindexcukup dipakai sebagai key. Daftar itu gak pernah berubah, jadi index aman di sini.
v-for atas object dan angka
const pesanan = reactive({ menu: "Nasi Goreng", jumlah: 2, catatan: "pedas" })
---template---
<ul>
<li v-for="(nilai, kunci, i) in pesanan" :key="kunci">{{ i }}. {{ kunci }}: {{ nilai }}</li>
</ul>
<button v-for="n in 3" :key="n">Meja {{ n }}</button>
| Yang ditulis | Hasilnya |
|---|---|
(nilai, kunci, i) in pesanan |
0. menu: Nasi Goreng, 1. jumlah: 2, 2. catatan: pedas |
n in 3 |
tombol Meja 1, Meja 2, Meja 3 |
- Pada object, nama pertama berisi nilai, kedua nama propertinya, ketiga index.
n in 3mengulang tiga kali, dan mulai dari 1, bukan 0.
Kenapa :key penting
const menu = ref([
{ id: 1, nama: "Nasi Goreng" },
{ id: 2, nama: "Mie Ayam" },
{ id: 3, nama: "Es Teh" },
])
function hapus(id) {
menu.value = menu.value.filter((m) => m.id !== id)
}
---template---
<ul>
<li v-for="(m, i) in menu" :key="i">
{{ m.nama }} <input placeholder="catatan" />
<button @click="hapus(m.id)">Habis</button>
</li>
</ul>
| Langkah | Dengan :key="i" |
Dengan :key="m.id" |
|---|---|---|
| Ketik "pedas" di baris Nasi Goreng | Nasi Goreng [pedas], Mie Ayam [ ], Es Teh [ ] | sama |
| Klik Habis di baris Nasi Goreng | Mie Ayam [pedas], Es Teh [ ] | Mie Ayam [ ], Es Teh [ ] |
Dengan :key="i", "pedas" pindah ke Mie Ayam. Itu jawaban tebakannya. Sesudah Nasi Goreng dihapus, Mie Ayam ada di posisi 0, jadi key-nya 0, sama dengan key baris Nasi Goreng tadi. Vue menyimpulkan baris ber-key 0 masih ada, memakai ulang <li> itu, dan cuma mengganti teksnya. Ketikan di input disimpan di elemennya, bukan di data, jadi ikut tertinggal. Yang dibuang malah <li> terakhir, ber-key 2.
Dengan :key="m.id", Vue melihat key 1 hilang dan membuang <li> Nasi Goreng beserta input-nya.
- Key harus unik dan tetap untuk item yang sama: biasanya id dari data.
- Index cuma aman untuk daftar yang gak pernah dihapus, disisipi, atau diurutkan ulang.
- Tanpa
:keysama sekali, Vue mencocokkan baris menurut urutan, sama dengan:key="i". Browser gak memberi warning apa pun; plugin ESLint resmi Vue (vue/require-v-for-key) yang menandainya.
📌 v-for dan v-if di elemen yang sama
---template---
<ul>
<li v-for="m in menu" v-if="m.stok > 0" :key="m.id">{{ m.nama }}</li>
</ul>
Di Vue 3, v-if dijalankan sebelum v-for, saat m belum ada. Render-nya berhenti dengan TypeError: Cannot read properties of undefined (reading 'stok'), dan seluruh komponen gak tampil. Build development juga menulis [Vue warn]: Property "m" was accessed during render but is not defined on instance. Saring dulu dengan computed:
const tersedia = computed(() => menu.value.filter((m) => m.stok > 0))
// template: <li v-for="m in tersedia" :key="m.id">{{ m.nama }}</li>
Atau pindahkan v-for ke <template v-for="m in menu" :key="m.id"> dan taruh <li v-if="m.stok > 0"> di dalamnya.
⚠️ Jebakan yang sering ketemu
- Index sebagai key di daftar yang berubah. Isian, fokus, atau animasi tertinggal di baris yang salah. Pakai id dari data.
v-fordi pembungkus.<ul v-for="item in buah">membuat tiga<ul>, masing-masing berisi satu<li>. Taruhv-fordi<li>.- Mengetik isinya langsung.
<li>Apel</li>tiga kali tampil sama, tapi gak ikut berubah saat datanya berubah. Latihan menolak<li>yang isinya diawali huruf, jadi isinya{{ item }}saja. v-ifdanv-fordi satu elemen.v-ifjalan duluan dan item-nya belum ada. Saring dengan computed, atau pakai<template v-for>.
TL;DR: <li v-for="m in menu" :key="m.id">{{ m.nama }}</li> membuat satu <li> per item; directive-nya di elemen yang diulang. (m, i) in menu memberi index mulai 0, (nilai, kunci, i) in object mengulang properti, dan n in 3 memberi 1, 2, 3. :key adalah label nama tiap baris: harus unik dan tetap, biasanya id dari data. Dengan index sebagai key, menghapus satu baris membuat isian input pindah ke baris berikutnya, karena Vue memakai ulang elemen menurut key-nya. v-for tanpa :key adalah kesalahan, dan v-if di elemen yang sama dijalankan duluan, jadi saring dulu dengan computed.