У Vue 3 на одному елементі v-if має вищий пріоритет, ніж v-for. Умова обчислюється раніше за цикл - і не має доступу до змінної циклу.
<!-- помилка: todo ще не визначено, коли перевіряється v-if -->
<li v-for="todo in todos" v-if="!todo.done">{{ todo.title }}</li>
У Vue 2 було навпаки (v-for першим), тож код, перенесений зі старих проєктів, ламається саме тут.
Правильні варіанти:
1. Фільтр - в обчислюваній властивості (найкращий варіант для фільтрації):
<script setup>
const activeTodos = computed(() => todos.value.filter((todo) => !todo.done));
</script>
<template>
<li v-for="todo in activeTodos" :key="todo.id">{{ todo.title }}</li>
</template>
Фільтр виконується лише при зміні todos, а не на кожен рендер, і шаблон стає простішим.
2. <template> з v-for зовні, v-if усередині - коли умова для кожного елемента окрема:
<template v-for="todo in todos" :key="todo.id">
<li v-if="!todo.done">{{ todo.title }}</li>
</template>
3. v-if зовні - для умови на весь список:
<ul v-if="todos.length">
<li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
</ul>
<p v-else>Завдань немає</p>
Що ще варто знати про v-for:
:keyобов'язковий - стабільний ідентифікатор, а не індекс;- перебір об'єкта:
v-for="(value, key, index) in object"; - діапазон:
v-for="n in 10"дає числа від 1 до 10; - на
<template>keyставиться на сам<template>.
Чому computed краще за фільтр у шаблоні: вираз v-for="todo in todos.filter(...)" створює новий масив на кожному рендері компонента - навіть якщо змінилося щось зовсім не пов'язане зі списком.