Отрисовка списков. Взаимодействие между v-for и v-if

Для вывода списка элементов во Vue предусмотрена специальная директива v-for. Допустим, у нас есть такой список:

<ul class="list">
  <li class="item">
    {{ user1.name }}
  </li>
  <li class="item">
    {{ user2.name }}
  </li>
  <li class="item">
    {{ user3.name }}
  </li>
</ul>

Количество элементов в нём может меняться динамически — элементы можно добавлять или удалять.

С помощью v-for этот список можно представить следующим образом:

<template>
    <ul class="list">
      <li
        v-for="user in list"
        class="item"
      >
        {{ user.name }}
      </li>
    </ul>
</template>
<script setup>
    const list = [
        { name: 'Иван' },
        { name: 'Мария' }
    ]
</script>

Директива v-for перебирает массив пользователей, и в результате количество элементов списка соответствует числу элементов в массиве.

У v-for есть второй необязательный параметр — индекс элемента. Получить его можно так:

<ul class="list">
  <li
    v-for="(user, index) in list"
    class="item"
  >
    {{ user.name }}
  </li>
</ul>

user и index доступны во всех вложенных элементах внутри v-for.

Вместо ключевого слова in допускается использовать of:

<li v-for="user of list">
...
</li>

Помимо массивов, v-for позволяет перебирать свойства объекта. Предположим, мы используем объект user:

object: {
  id: 1,
  name: 'User Test',
  email: 'test@gmail.com'
}

Значения свойств объекта можно вывести списком следующим образом:

<template>
  <ul class="list">
    <li
      v-for="value in obj"
      class="item"
    >
      {{ value }}
    </li>
  </ul>
</template>

<script setup>
  const obj = {
    id: 1,
    name: 'User Test',
    email: 'test@gmail.com'
  }
</script>

При итерации по объекту доступны два дополнительных параметра: ключ свойства и индекс:

<ul class="list">
  <li
    v-for="(value, key, index) in obj"
    class="item"
  >
   {{ index }} - {{ key }} - {{ value }}
  </li>
</ul>

Названия параметров value, key и index выбраны для наглядности — их можно заменить на любые другие.

Для идентификации каждого элемента списка необходимо указывать атрибут key:

<ul class="list">
  <li
    v-for="user in list"
    :key="user.id"
    class="item"
  >
    {{ user.name }}
  </li>
</ul>

Если список динамический, уникальный id элемента — оптимальный выбор для key. Если список статический и у элементов нет id, можно воспользоваться индексом.

Всегда указывайте атрибут key с v-for. Исключение — когда итерируемый контент DOM прост или вы сознательно применяете стратегию обновления по умолчанию для повышения производительности.

Отображение отфильтрованных и отсортированных результатов

Иногда список необходимо отсортировать или отфильтровать. В таких случаях лучше не изменять исходный массив, а создать на его основе новый с помощью вычисляемого свойства:

Options API

<script>
export default {
  data() {
    return {
      originalList: [
        { id: 1, visible: true },
        { id: 2, visible: false }
      ]
    }
  },
  computed: {
    filteredList() {
      return this.originalList.filter(item => item.visible)
    }
  }
}
</script>

Composition API

<script setup>
import { reactive, computed } from 'vue'

const originalList = reactive([
  { id: 1, visible: true },
  { id: 2, visible: false }
])

const filteredList = computed(() => originalList.filter(item => item.visible))
</script>

Далее в шаблоне используется отфильтрованный список. Если создать вычисляемое свойство невозможно, можно вызвать метод непосредственно в шаблоне:

<ul class="list">
  <li
    v-for="user in filterList(list)"
    :key="user.id"
    class="item"
  >
    {{ user.name }}
  </li>
</ul>

Нередко возникает необходимость на основе свойств элемента списка выполнить определённую логику — например, сформировать набор CSS-классов. Это тоже реализуется через вызов метода:

<ul class="list">
  <li
    v-for="user in list"
    :key="user.id"
    class="item"
    :class="getItemClasses(user)"
  >
    {{ user.name }}
  </li>
</ul>

Генерация уникальных ID

В версии Vue 3.5 появилась встроенная функция useId. Она позволяет создавать уникальные идентификаторы, которые остаются стабильными при повторном рендеринге.

<script>
import { useId } from 'vue'
const id = useId()
</script>

<template>
  <form>
    <label :for="id">Name:</label>
    <input :id="id" type="text">
  </form>
</template>

v-for и диапазоны

В директиву можно передать целое число, и тогда элемент будет отрисован указанное количество раз:

<ul class="list">
  <li v-for="n in 10">
    {{ n }}
  </li>
</ul>

v-for и тег template

Когда внутри v-for нужно отрисовать несколько элементов без дополнительной обёртки, используйте тег <template>:

<ul class="list">
  <template v-for="user in list">
    <li>{{ user.name }}</li>
    <li class="info">Дополнительная информация...</li>
  </template>
</ul>

v-for и v-if

Важно: не используйте эти директивы на одном элементе, поскольку приоритет v-if выше, чем у v-for. Подробнее об этом описано в документации.

Компоненты и v-for

Директива совместима с пользовательскими компонентами:

<ul class="list">
  <my-user-component
    v-for="user in list"
    :key="user.id"
    class="item"
    :user="user"
  />
</ul>

Проверьте себя

1.Почему нельзя использовать v-if и v-for на одном элементе?

2.Зачем нужен атрибут key при использовании v-for?

3.Как лучше отображать отфильтрованный список, не изменяя исходный массив?