Рендеринг списков и Vue директива v-for

Рендеринг списков и Vue директива v-for

От автора: рендеринг списков – одна из наиболее используемых практик в front end разработке. Динамический рендеринг списков часто используется для представления серии схожей сгруппированной информации в сжатом и дружелюбном для пользователя формате. Почти в каждом используемом нам веб-приложении можно найти списки контента во множестве областей. В этой статье я расскажу про директиву Vue v for при создании динамических списков, покажу пару примеров о том, почему для этого нужно использовать атрибут key. Мы все будем подробно объяснять при написании кода. Статья предполагает, что у вас нет знаний по Vue, или вы знаете совсем немного.

Пример: Twitter

В качестве примера для статьи возьмем Twitter. После авторизации на главном роуте index в Twitter нам открывается следующий вид:

Рендеринг списков и Vue директива v-for

На домашней странице мы привыкли видеть список трендов, список твитов, список возможных фоловеров и т.д. Контент в этих списках зависит от множества факторов – наши история в Twitter, наши подписки, наши лайки и т.д. В общем, можно сказать, все эти данные динамические.

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

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее

Например, список твитов – это список элементов tweet-component. tweet-component можно представить, как оболочку, которая собирает данные (username, id пользователя, твит и аватар) из других частей и просто отображает в единообразной разметке.

Рендеринг списков и Vue директива v-for

Скажем, нам нужно отрендерить список компонентов (например, список элементов tweet-component) на основе большого объема данных, полученных с сервера. Первое, что должно прийти на ум в Vue, это директива v-for.

Директива v-for

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

Рендеринг списков и Vue директива v-for

Разберем пример. Предполагаем, что уже получили коллекцию твитов:

const tweets = [
  {
 id: 1,
 name: 'James',
 handle: '@jokerjames',
 img: 'https://semantic-ui.com/images/avatar2/large/matthew.png',
 tweet: "If you don't succeed, dust yourself off and try again.",
 likes: 10,
  },
  { 
 id: 2,
 name: 'Fatima',
 handle: '@fantasticfatima',
 img: 'https://semantic-ui.com/images/avatar2/large/molly.png',
 tweet: 'Better late than never but never late is better.',
 likes: 12,
  },
  {
 id: 3,
 name: 'Xin',
 handle: '@xeroxin',
 img: 'https://semantic-ui.com/images/avatar2/large/elyse.png',
 tweet: 'Beauty in the struggle, ugliness in the success.',
 likes: 18,
  }
]

Tweets – это коллекция объектов tweet. В каждом tweet содержится детальная информация одного твита – уникальный id, имя, id профиля, сообщение и т.д. Теперь давайте попробуем с помощью директивы v-for отрендерить список компонентов tweet на основе этих данных.

Прежде всего создаем объект Vue – сердце приложения Vue. Мы подключим/прицепим наш объект к DOM элементу с id app и присвоим коллекцию tweets, как часть объекта данных объекта Vue.

new Vue({
  el: '#app',
  data: {
 tweets
  }
});

Теперь создадим tweet-component, который будет использовать директива v-for для рендера списка. Используем глобальный конструктор Vue.component для создания компонента tweet-component:

Vue.component('tweet-component', {
  template: ` 
 <div class="tweet">
 <div class="box">
 <article class="media">
 <div class="media-left">
 <figure class="image is-64x64">
 <img :src="tweet.img" alt="Image">
 </figure>
 </div>
 <div class="media-content">
 <div class="content">
 <p>
 <strong>{{tweet.name}}</strong> <small>{{tweet.handle}}</small>
 <br>
 {{tweet.tweet}}
 </p>
 </div>
 <div class="level-left">
 <a class="level-item">
 <span class="icon is-small"><i class="fas fa-heart"></i></span>
 <span class="likes">{{tweet.likes}}</span>
 </a>
 </div>
 </div>
 </article>
 </div>
 </div> 
  `,
  props: {
 tweet: Object
  }
});

Что здесь стоит отметить:

tweet-component ожидает prop объект tweet, как видно из требования валидации prop (props: {tweet: Object}). Если компонент рендерится с tweet prop, который не является объектом, Vue пропустит предупреждения.

Мы привязываем свойства prop объекта tweet к шаблону компонента с помощью синтаксиса {{ }}.

Разметка компонента адаптирует элемент Bulma Box, так как он сильно похож на твит.

В HTML шаблоне нужно создать разметку, куда будет подключаться наше Vue приложение (например, элемент с id app). Внутри этой разметку мы будем использовать v-for для рендера списка твитов. Tweets – это коллекция данных, по которой мы будем бегать в цикле. Поэтому подходящим алиасом для использования в директиве будет tweet. В каждый отрендереный tweet-component будем передавать объект tweet из цикла в виде props, чтобы он был доступен в компоненте.

<div id="app" class="columns">
  <div class="column">
 <tweet-component v-for="tweet in tweets" :tweet="tweet"/>
  </div>
</div>

Независимо от количества объектов tweet в коллекции и того, как они могут меняться со временем, наш код всегда будет рендерить все твиты из коллекции в той же разметке. Добавим немного своего CSS, и наше приложение выглядит примерно так:

Все работает, как надо, но в консоли можно обнаружить совет от Vue:

[Vue tip]: <tweet-component v-for="tweet in tweets">: component lists rendered with v-for should have explicit keys...

При запуске кода на CodePen вы можете не увидеть предупреждения в консоли браузера.

Почему Vue говорит нам указать явные ключи списка, когда все работает?

Key

Для элементов в цикле принято добавлять атрибут key внутри списка v-for. Vue использует атрибут key для создания уникальной привязки для каждой идентичности узла.

Давайте разъясним – если в наш список будут внесены UI изменения (например, перемешается порядок элементов), Vue будет менять данные в каждом элементе, а не двигать DOM элементы. В большинстве случаев это не будет проблемой. Тем не менее, в определенных сценариях, где список v-for зависит от состояния DOM и/или состояния дочернего компонента, это может привести к нежелательному поведению.

Разберем пример. Что, если бы наш простой компонент твита содержал поле ввода, позволяющее пользователю автоматически отвечать на твит? Не будем вдаваться в то, как этот ответ будет отправлен, просто обратимся к новому полю ввода:

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее

Рендеринг списков и Vue директива v-for

Добавим новое поле ввода в шаблон tweet-component:

Vue.component('tweet-component', {
  template: `
 <div class="tweet">
 <div class="box">
 // ...
 </div>
 <div class="control has-icons-left has-icons-right">
 <input class="input is-small" placeholder="Tweet your reply..." />
 <span class="icon is-small is-left">
 <i class="fas fa-envelope"></i>
 </span>
 </div>
 </div>
  `,
  props: {
 tweet: Object
  }
});

Предположим, что хотели добавить новую функцию в приложение. Эта функция позволяет перемешивать случайным образом список твитов. Для этого сначала нужно подключить кнопку Shuffle! в HTML шаблоне:

<div id="app" class="columns">
  <div class="column">
 <button class="is-primary button" @click="shuffle">Shuffle!</button>
 <tweet-component  v-for="tweet in tweets" :tweet="tweet"/>
  </div>
</div>

Мы подключили обработчик события click на кнопку. По клику вызывается метод shuffle. В объекте Vue создадим метод shuffle, который будет случайно перемешивать коллекцию tweets в объекте. Для этого используем метод lodash _shuffle:

new Vue({
  el: '#app',
  data: {
 tweets
  },
  methods: {
 shuffle() {
 this.tweets = _.shuffle(this.tweets)
 }
  }
});

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

Но если ввести текст в поля компонентов и нажать shuffle, происходит нечто необычное:

Рендеринг списков и Vue директива v-for

Мы не стали использовать атрибут key, поэтому Vue не создал уникальные привязки к узлам твитов. Как результат, когда мы хотим отсортировать твиты, Vue идет самым быстрым способом и просто меняет данные во всех элементах. Временное состояние DOM (например, введенный текст) не затрагивается, поэтому нам это не подходит.

На рисунке показано, как во всех элементах меняются данные, а состояние DOM нет:

Рендеринг списков и Vue директива v-for

Чтобы решить эту проблему, нам нужно присвоить уникальный key всем tweet-component, рендерящимся в списке. В качестве уникального идентификатора возьмем id tweet, так как мы точно знаем, что они все разные. Мы используем динамические значения, поэтому используем директиву v-bind для привязки нашего key к tweet.id:

<div id="app" class="columns">
  <div class="column">
 <button class="is-primary button" @click="shuffle">Shuffle!</button>
 <tweet-component  v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" />
  </div>
</div>

Теперь Vue видит каждую идентичность узла твита и будет правильно сортировать компоненты при перемешивании списка.

Рендеринг списков и Vue директива v-for

Теперь твиты перемещаются правильно. Можно показать, как именно сортируются элементы с помощью Vue transition-group.

Для этого добавим элемент transition-group как обертку к списку v-for. Укажем имя перехода tweets и объявим, что группа перехода должна рендериться как тег div.

<div id="app" class="columns">
  <div class="column">
 <button class="is-primary button" @click="shuffle">Shuffle!</button>
 <transition-group name="tweets" tag="div">
 <tweet-component  v-for="tweet in tweets" :tweet="tweet" :key="tweet.id" />
 </transition-group>
  </div>
</div>

По имени перехода Vue автоматически распознает любые установленные CSS переходы/анимации. Наша цель – переместить элементы в списке с помощью перехода. Поэтому Vue будет искать CSS переход в строках tweets-move (где tweets – имя, указанное в transition group). Как результат, мы вручную вводим класс .tweets-move, в котором задан определенный тип и время перехода:

#app .tweets-move {
  transition: transform 1s;
}

Это очень краткий пример применения переходов к спискам. Посмотреть все типы переходов можно в документации Vue.

Теперь наши элементы tweet-component будут плавно менять положение при перемешивании. Попробуйте! Введите что-нибудь в поля ввода и кликните Shuffle! несколько раз.

Круто, правда? Без атрибута key мы бы не смогли использовать элемент transition-group для создания перехода в списке, так как вместо элементов менялись бы данные в них.

Стоит ли всегда использовать атрибут key? Рекомендуется. Спецификация Vue утверждает, что атрибут key не стоит использовать только:

Нам специально нужна стандартная замена элементов для повышения производительности

Контента DOM просто достаточно

Заключение

Вот и все! Надеюсь, эта короткая статья показала полезность директивы v-for и объяснила, почему атрибут key так часто используется. Если у вас возникли вопросы/мысли, дайте мне знать.

Автор: Hassan Djirdeh

Источник: https://css-tricks.com/

Редакция: Команда webformyself.

Фреймворк VUE JS: быстрый старт, первые результаты

Получите бесплатный курс и создайте веб-приложение на трендовой Frontend-технологии VUE JS с полного нуля

Узнать подробнее

VUE JS. Быстрый старт

Практический курс по созданию веб-приложения на VUE JS с полного нуля

Получить

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree