# Отрисовка списков
# Отображение массива элементов с помощью 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>
2
3
4
5
Vue.createApp({
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}).mount('#array-rendering')
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>
2
3
4
5
Vue.createApp({
data() {
return {
parentMessage: 'Родитель',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
}
}).mount('#array-with-index')
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>
# v-for
для объекта
v-for
можно также использовать для итерирования по свойствам объекта:
<ul id="v-for-object" class="demo">
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
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')
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>
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>
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>
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/))
Можно предположить, что Vue придётся выбросить существующий DOM и заново отрисовать список целиком, но, к счастью, это не так. Во Vue есть умные эвристики для максимизации переиспользования элементов DOM, поэтому замена одного массива другим, в случае совпадения части элементов этих массивов, будет очень эффективной операцией.
# Отображение отфильтрованных/отсортированных результатов
Иногда хочется отобразить отфильтрованную или отсортированную версию массива, не изменяя оригинальные данные. В таком случае создайте вычисляемое свойство, возвращающее отфильтрованный или отсортированный массив.
Например:
<li v-for="n in evenNumbers" :key="n">{{ n }}</li>
data() {
return {
numbers: [ 1, 2, 3, 4, 5 ]
}
},
computed: {
evenNumbers() {
return this.numbers.filter(number => number % 2 === 0)
}
}
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>
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)
}
}
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>
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>
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>
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>
2
3
4
5
# Компоненты и v-for
Эта секция подразумевает, что вы уже знакомы с компонентами. Не стесняйтесь пропустить её сейчас и вернуться потом.
Можно использовать v-for
на компонентах, как и на обычных элементах:
<my-component v-for="item in items" :key="item.id"></my-component>
Однако, в компонент никакие данные не передаются автоматически, поскольку у компонентов изолированные области видимости. Для передачи итерируемых данных в компонент необходимо явно использовать входные параметры:
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
></my-component>
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>
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')
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.