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

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

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

Пример: Twitter

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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, чтобы он был доступен в компоненте.

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

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

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

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

Key

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Заключение

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

Автор: Hassan Djirdeh

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

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

Метки:

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

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