Отрисовка списков
Отображение элементов массива через v-for
Используйте директиву v-for для отрисовки списка элементов на основе массива данных. У директивы v-for специальный синтаксис: item in items, где items — исходный массив, а item — ссылка на итерируемый элемент массива:
js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])template
<li v-for="item in items">
{{ item.message }}
</li>Внутри блока v-for доступны все свойства из области видимости родителя. Также может быть второй опциональный параметр у v-for с индексом текущего элемента:
js
const parentMessage = ref('Родитель')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])template
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>Область видимости переменных в v-for аналогична следующему коду JavaScript:
js
const parentMessage = 'Родитель'
const items = [
/* ... */
]
items.forEach((item, index) => {
// есть доступ к внешней области видимости и `parentMessage`
// но переменные `item` и `index` доступны только здесь
console.log(parentMessage, item.message, index)
})Обратите внимание, что значения внутри директивы v-for совпадают с сигнатурой функции коллбэка forEach. Вообще-то можно использовать деструктуризацию на переменной текущего элемента, аналогично деструктуризации аргументов функции:
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- при использовании переменой для индекса -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>Для вложенных v-for область видимости работает аналогично вложенным функциям. Каждая область видимости v-for имеет доступ к родительским областям видимости:
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>Можно использовать of в качестве разделителя вместо in, как в синтаксисе итераторов JavaScript:
template
<div v-for="item of items"></div>Отображение свойств объекта через v-for
Вы также можете использовать v-for для итерации по свойствам объекта. Порядок итерации будет основан на результате вызова Object.values() для объекта:
js
const myObject = reactive({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
})template
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>Можно указать второй аргумент для получения имени свойства (ключа объекта):
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>И третий — для индекса:
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>v-for и диапазоны
Можно передавать целое число в v-for — шаблон будет повторяться указанное число раз.
template
<span v-for="n in 10">{{ n }}</span>Обратите внимание, что в таком случае значения n начинаются с 1, а не с 0.
v-for и <template>
Аналогично использованию с v-if, также можно использовать тег <template> с директивой v-for для отрисовки блоков из нескольких элементов. Например:
template
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>v-for и v-if
Когда они указаны вместе на одном узле, у v-if будет больший приоритет, чем у v-for. И поэтому в условии v-if не будет доступа к переменным из области видимости v-for:
template
<!--
Будет выброшена ошибка, так как свойство "todo" не объявлено в экземпляре.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>Это можно исправить, для ясности переместив v-for на тег-обёртку <template>:
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>Примечание
Не рекомендуется использовать v-if и v-for на одном и том же элементе из-за неявного приоритета.
Есть два распространенных случая, когда это может быть заманчиво:
Чтобы отфильтровать элементы в списке (например
v-for="user in users" v-if="user.isActive"). В этих случаях, заменитеusersна новое вычисляемое свойство, которое возвращает ваш отфильтрованный список (напримерactiveUsers).Чтобы не отображать список, если он должен быть скрыт (например
v-for="user in users" v-if="shouldShowUsers"). В этих случаях, переместитеv-ifв элемент контейнера (напримерul,ol).
Сохранение состояния с помощью key
При обновлении Vue списка элементов, отрисованного директивой v-for, по умолчанию используется стратегия обновления «на месте». Если порядок элементов массива или объекта изменился, Vue не станет перемещать элементы DOM, а просто обновит каждый элемент «на месте», чтобы он отображал новые данные по соответствующему индексу.
Режим по умолчанию эффективен, но применим только в случаях, когда результат отрисовки списка не полагается на состояние дочерних компонентов или временное состояние DOM (например, значения в полях форм).
Чтобы подсказать Vue, как определять идентичность каждого элемента, и, таким образом, переиспользовать и упорядочивать существующие элементы, необходимо указать уникальный атрибут key для каждого элемента:
template
<div v-for="item in items" :key="item.id">
<!-- Содержимое -->
</div>При использовании <template v-for> атрибут key нужно устанавливать на контейнер <template>:
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>Примечание
key в данном случае — специальный атрибут, связываемый через v-bind. Его не следует путать с переменной с именем key при использовании v-for с объектами.
Рекомендуется всегда указывать атрибут key с v-for, кроме случаев когда итерируемое содержимое DOM простое (т.е. не содержит компонентов или элементов DOM с состоянием), или когда сознательно полагаетесь на стратегию обновления по умолчанию для улучшения производительности.
Привязка key ожидает использования примитивных значений — строк и чисел. Не используйте объекты в качестве ключей для v-for. Подробное использование атрибута key описано в документации API.
v-for и компоненты
Эта секция подразумевает, что уже знакомы с компонентами. Не стесняйтесь пропустить её сейчас и вернуться потом.
Можно использовать v-for на компонентах, как на обычных элементах (не забывайте указать key):
template
<MyComponent v-for="item in items" :key="item.id" />Однако в компонент никакие данные автоматически передаваться не будут, поскольку у каждого будет своя изолированная область видимости. Чтобы передать итерируемые данные в компонент потребуется явно использовать входные параметры:
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>Причина, почему не происходит автоматической передачи item в компонент заключается в том, что это сделает компонент жёстко связанным с тем, как работает v-for. Но явное указание на то, откуда поступают данные, позволит переиспользовать и в других ситуациях.
Посмотрите этот пример простого TODO-списка, чтобы увидеть, как отрисовать список компонентов с помощью v-for, передавая разные данные каждому экземпляру.
Отслеживание изменений в массивах
Методы изменения, мутирующие массив
Vue способен определять, когда вызываются методы мутации реактивного массива, и запускать необходимые обновления. К таким мутирующим методам относятся:
push()pop()shift()unshift()splice()sort()reverse()
Методы изменения с заменой массива
Методы, мутирующие массив, как следует из названия, будут изменять исходный массив, на котором они вызваны. Но есть и другие, например filter(), concat() и slice(), которые не мутируют исходный массив, а всегда возвращают новый массив. При их использовании можно просто заменять старый массив на новый:
js
// `items` это ref-ссылка с массивом в значении
items.value = items.value.filter((item) => item.message.match(/Foo/))Может показаться, что в таких случаях Vue придётся выбросить существующий DOM и заново отрисовать весь список — к счастью, это не так. Во Vue есть умные эвристики для максимизации переиспользования элементов DOM, поэтому замена одного массива другим, в случае совпадения части элементов этих массивов, будет очень эффективной операцией.
Отображение отфильтрованных/отсортированных результатов
Иногда может потребоваться отображать отфильтрованную или отсортированную версию массива, сохранив оригинальные данные. В таком случае можно создать вычисляемое свойство, которое будет возвращать отфильтрованный или отсортированный массив.
Например:
js
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((n) => n % 2 === 0)
})template
<li v-for="n in evenNumbers">{{ n }}</li>В ситуациях, когда вычисляемые свойства невозможно применить (например, внутри вложенных циклов v-for), можно использовать метод:
js
const sets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
function even(numbers) {
return numbers.filter((number) => number % 2 === 0)
}template
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>Обратите внимание на использование reverse() и sort() в вычисляемых свойствах! Эти два метода изменят исходный массив, а этого следует избегать в геттерах вычисляемых свойств. Поэтому перед вызовом этих методов создайте копию исходного массива:
diff
- return numbers.reverse()
+ return [...numbers].reverse()