Отрисовка списков
Отображение элементов массива через 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()