Почему не стоит использовать директивы v-for и v-if на одном и том же элементе?vue-9

Использование v-for и v-if на одном элементе считается антипаттерном во Vue.js. Разберем причины этого и правильные альтернативы.

Основные проблемы

1. Приоритет директив

Во Vue 2.x v-for имеет более высокий приоритет, чем v-if. Это означает:

<!-- Плохая практика -->
<div v-for="item in items" v-if="item.isActive">
  {{ item.name }}
</div>

Фактически выполняется как:

  1. Сначала создаются ВСЕ элементы списка
  2. Затем применяется фильтрация через v-if

2. Проблемы производительности

  • Ненужные вычисления: Vue создает виртуальные узлы для всех элементов, даже скрытых
  • Лишние рендеры: DOM-операции выполняются для элементов, которые не будут отображены
  • Потеря преимуществ реактивности: Изменения в items вызывают полный перерендер

3. Неочевидное поведение

  • Сложнее отслеживать изменения данных
  • Может вызывать неожиданные сайд-эффекты
  • Усложняет отладку компонентов

Правильные альтернативы

1. Использование computed-свойства

Лучший подход - предварительная фильтрация данных:

computed: {
  activeItems() {
    return this.items.filter(item => item.isActive)
  }
}
<div v-for="item in activeItems" :key="item.id">
  {{ item.name }}
</div>

2. Обертка в <template>

Если computed не подходит:

<template v-for="item in items">
  <div v-if="item.isActive" :key="item.id">
    {{ item.name }}
  </div>
</template>

3. Метод в v-for

Для простых случаев (менее предпочтительно):

<div v-for="item in items.filter(i => i.isActive)" :key="item.id">
  {{ item.name }}
</div>

Особенности Vue 3

В Vue 3 приоритеты изменились:

  • Теперь v-if имеет более высокий приоритет, чем v-for
  • Но совместное использование все равно вызывает warning в консоли
  • Официальная рекомендация - избегать такого сочетания

Примеры сравнения

Неправильно:

<ul>
  <li v-for="user in users" v-if="user.isActive" :key="user.id">
    {{ user.name }}
  </li>
</ul>

Правильно:

computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
<ul>
  <li v-for="user in activeUsers" :key="user.id">
    {{ user.name }}
  </li>
</ul>

Когда можно нарушить правило

Единственное исключение - когда нужно:

  1. Пропустить рендеринг всего списка при определенных условиях
  2. И это условие не зависит от элементов списка

Пример допустимого использования:

<ul v-if="shouldShowList">
  <li v-for="item in items" :key="item.id">
    {{ item.name }}
  </li>
</ul>

Производительность

Разница в производительности:

  • С computed: O(n) операций (один проход фильтрации)
  • С v-for+v-if: O(n) + O(m) операций (рендер + условная проверка)
  • При больших списках разница становится существенной

Резюмируем:

Совместное использование v-for и v-if на одном элементе снижает производительность, усложняет поддержку кода и может привести к неожиданным результатам. Всегда предпочитайте предварительную фильтрацию данных через computed-свойства или разделяйте директивы с помощью <template>.