# Отрисовка списков

# Отображение массива элементов с помощью v-for

Используйте директиву v-for для отрисовки списка элементов на основе массива данных. У директивы v-for особый синтаксис записи: item in items, где items — исходный массив, а itemссылка на текущий элемент массива:

<ul id="array-rendering">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
1
2
3
4
5
Vue.createApp({
  data() {
    return {
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }
}).mount('#array-rendering')
1
2
3
4
5
6
7

Результат:

See the Pen v-for with Array by Vue (@Vue) on CodePen.

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

<ul id="array-with-index">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
1
2
3
4
5
Vue.createApp({
  data() {
    return {
      parentMessage: 'Родитель',
      items: [
        { message: 'Foo' },
        { message: 'Bar' }
      ]
    }
  }
}).mount('#array-with-index')
1
2
3
4
5
6
7
8
9
10
11

Результат:

See the Pen v-for with Array and index by Vue (@Vue) on CodePen.

Вместо in разделителем можно использовать of, как в итераторах JavaScript:

<div v-for="item of items"></div>
1

# v-for для объекта

v-for можно также использовать для итерирования по свойствам объекта:

<ul id="v-for-object" class="demo">
  <li v-for="value in myObject">
    {{ value }}
  </li>
</ul>
1
2
3
4
5
Vue.createApp({
  data() {
    return {
      myObject: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2016-04-10'
      }
    }
  }
}).mount('#v-for-object')
1
2
3
4
5
6
7
8
9
10
11

Результат:

See the Pen v-for with Object by Vue (@Vue) on CodePen.

Можно использовать второй аргумент для получения имени свойства (ключа объекта):

<li v-for="(value, name) in myObject">
  {{ name }}: {{ value }}
</li>
1
2
3

See the Pen v-for with Object and key by Vue (@Vue) on CodePen.

И третий — для индексов:

<li v-for="(value, name, index) in myObject">
  {{ index }}. {{ name }}: {{ value }}
</li>
1
2
3

See the Pen v-for with Object key and index by Vue (@Vue) on CodePen.

Примечание

При итерировании по объекту порядок обхода такой же как и в Object.keys(). Его консистентность не гарантируется при использовании различных реализаций движков JavaScript.

# Сохранение состояния

При обновлении Vue списка элементов, отображаемого директивой v-for, по умолчанию используется стратегия обновления «на месте». Если порядок элементов массива или объекта изменился, Vue не станет перемещать элементы DOM, а просто обновит каждый элемент «на месте», чтобы он отображал новые данные по соответствующему индексу.

Режим по умолчанию эффективен, но применим только в случаях, когда результат отрисовки вашего списка не полагается на состояние дочерних компонентов или временные состояния DOM (например, значения полей форм).

Чтобы подсказать Vue, как отслеживать идентичность каждого элемента, что позволит переиспользовать и перемещать существующие элементы, укажите уникальный атрибут key для каждого элемента:

<div v-for="item in items" :key="item.id">
  <!-- содержимое -->
</div>
1
2
3

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

Поскольку этот механизм является общим для идентификации элементов во Vue, имеются и другие варианты применения key, не связанные явно с v-for, как мы увидим далее в руководстве.

Совет

Не указывайте в качестве ключей v-for непримитивные значения. Вместо этого используйте строковые или числовые значения.

Подробнее про использование атрибута key можно прочитать в документации API.

# Отслеживание изменений в массивах

# Методы внесения изменений

Vue оборачивает у наблюдаемого массива методы внесения изменений, чтобы они вызывали обновления представления. Оборачиваются следующие методы:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

Можете открыть консоль и поиграть с массивом items из предыдущего примера, вызывая его методы внесения изменений, например: example1.items.push({ message: 'Baz' }).

# Замены в массиве

Методы внесения изменений, как следует из их названия, изменяют оригинальный массив, на котором они вызываются. Существуют и неизменяющие методы, такие как filter(), concat() и slice(), они не вносят изменений в изначальный массив, а всегда возвращают новый массив. При работе с неизменяющими методами можно просто заменять старый массив на новый:

example1.items = example1.items.filter(item => item.message.match(/Foo/))
1

Можно предположить, что Vue придётся выбросить существующий DOM и заново отрисовать список целиком, но, к счастью, это не так. Во Vue есть умные эвристики для максимизации переиспользования элементов DOM, поэтому замена одного массива другим, в случае совпадения части элементов этих массивов, будет очень эффективной операцией.

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

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

Например:

<li v-for="n in evenNumbers" :key="n">{{ n }}</li>
1
data() {
  return {
    numbers: [ 1, 2, 3, 4, 5 ]
  }
},
computed: {
  evenNumbers() {
    return this.numbers.filter(number => number % 2 === 0)
  }
}
1
2
3
4
5
6
7
8
9
10

В ситуациях, когда вычисляемые свойства невозможно использовать (например, внутри вложенных циклов v-for), просто используйте метод:

<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)" :key="n">{{ n }}</li>
</ul>
1
2
3
data() {
  return {
    sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
  }
},
methods: {
  even(numbers) {
    return numbers.filter(number => number % 2 === 0)
  }
}
1
2
3
4
5
6
7
8
9
10

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

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

<div id="range" class="demo">
  <span v-for="n in 10" :key="n">{{ n }}</span>
</div>
1
2
3

Результат:

See the Pen v-for with a range by Vue (@Vue) on CodePen.

# v-for и тег template

Подобно использованию с v-if, также можно использовать тег <template> с директивой v-for для отображения блока из нескольких элементов. Например:

<ul>
  <template v-for="item in items" :key="item.msg">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>
1
2
3
4
5
6

# v-for и v-if

Совет

Обратите внимание, что не рекомендуется использовать вместе v-if и v-for. Подробнее в разделе рекомендаций.

Когда присутствуют вместе на одном элементе, v-if имеет больший приоритет, чем v-for. Это означает, что условие v-if не будет иметь доступа к переменным из области видимости v-for:

<!-- Это приведёт к ошибке, так как свойство "todo" не определено в экземпляре. -->

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>
1
2
3
4
5

Это можно исправить, переместив v-for на тег-обёртку <template>:

<template v-for="todo in todos" :key="todo.name">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>
1
2
3
4
5

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

Эта секция подразумевает, что вы уже знакомы с компонентами. Не стесняйтесь пропустить её сейчас и вернуться потом.

Можно использовать v-for на компонентах, как и на обычных элементах:

<my-component v-for="item in items" :key="item.id"></my-component>
1

Однако, в компонент никакие данные не передаются автоматически, поскольку у компонентов изолированные области видимости. Для передачи итерируемых данных в компонент необходимо явно использовать входные параметры:

<my-component
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
></my-component>
1
2
3
4
5
6

Причина, почему item не передаётся в компонент автоматически, в том, что это сделает компонент жёстко связанным с логикой работы v-for. Но если указывать источник данных явно, компонент можно будет использовать и в других ситуациях.

Ниже приведён полный пример простого списка задач:

<div id="todo-list-example">
  <form v-on:submit.prevent="addNewTodo">
    <label for="new-todo">Добавить задачу</label>
    <input
      v-model="newTodoText"
      id="new-todo"
      placeholder="Например, покормить кота"
    />
    <button>Добавить</button>
  </form>
  <ul>
    <todo-item
      v-for="(todo, index) in todos"
      :key="todo.id"
      :title="todo.title"
      @remove="todos.splice(index, 1)"
    ></todo-item>
  </ul>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const app = Vue.createApp({
  data() {
    return {
      newTodoText: '',
      todos: [
        {
          id: 1,
          title: 'Do the dishes'
        },
        {
          id: 2,
          title: 'Take out the trash'
        },
        {
          id: 3,
          title: 'Mow the lawn'
        }
      ],
      nextTodoId: 4
    }
  },
  methods: {
    addNewTodo() {
      this.todos.push({
        id: this.nextTodoId++,
        title: this.newTodoText
      })
      this.newTodoText = ''
    }
  }
})

app.component('todo-item', {
  template: `
    <li>
      {{ title }}
      <button @click="$emit('remove')">Удалить</button>
    </li>
  `,
  props: ['title'],
  emits: ['remove']
})

app.mount('#todo-list-example')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

See the Pen v-for with components by Vue (@Vue) on CodePen.

Deployed on Netlify.
Последнее обновление: 2021-01-18, 21:10:40 UTC