v-for itera sobre arrays, objetos, números y strings para renderizar una lista de elementos. Funciona como un bucle for...of en JavaScript, pero dentro del template.
Arrays
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
])
</script>El segundo argumento te da el índice:
<li v-for="(item, index) in items" :key="item.id">
{{ index }}. {{ item.name }}
</li>Objetos
<template>
<div v-for="(value, key, index) in user" :key="key">
{{ index }}. {{ key }}: {{ value }}
</div>
</template>
<script setup>
const user = { name: 'Ana', role: 'Dev', level: 'Senior' }
</script>Rangos
<!-- Renderiza del 1 al 5 -->
<span v-for="n in 5" :key="n">{{ n }}</span>Por qué importa :key
Sin :key, Vue reutiliza los elementos del DOM por posición. Esto falla cuando los elementos se reordenan, se eliminan o se insertan en medio, porque el state del componente y el state del DOM se dessincronizan.
<!-- Incorrecto: el índice como key tiene el mismo problema que ninguna key al reordenar -->
<li v-for="(item, index) in items" :key="index">...</li>
<!-- Correcto: identificador único y estable -->
<li v-for="item in items" :key="item.id">...</li>v-for en template
Cuando necesitas renderizar varios elementos por iteración sin un wrapper:
<template>
<ul>
<template v-for="item in items" :key="item.id">
<li>{{ item.name }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
</template>v-for con componentes
<template>
<UserCard
v-for="user in users"
:key="user.id"
:user="user"
@remove="removeUser(user.id)"
/>
</template>Las props no se inyectan automáticamente desde la iteración. Tienes que enlazarlas explícitamente.
Mutar vs reemplazar arrays
Vue detecta las llamadas a métodos de mutación (push, pop, shift, unshift, splice, sort, reverse) en arrays reactivos y actualiza el DOM. Para los métodos no mutadores (filter, map, concat), asigna el resultado de vuelta al ref:
// Mutación: Vue lo detecta
items.value.push({ id: 4, name: 'Date' })
// Reemplazo: asigna el nuevo array
items.value = items.value.filter(i => i.name !== 'Banana')Ver también: ¿Para qué sirve :key en v-for?
Referencias
- Renderizado de Listas - Docs de Vue.js
- v-for - Docs de Vue.js