Skip to content

Рендеринг списків

v-for

Ми можемо використовувати директиву v-for для рендерингу списку елементів, основаних на масиві. Директив v-for вимагає спеціального синтаксису у формі item in items, де items — вихідний масив і item є псевдонімом ітерованого елемента масиву:

js
const items = ref([{ message: 'Леся' }, { message: 'Тарас' }])
js
data() {
  return {
    items: [{ message: 'Леся' }, { message: 'Тарас' }]
  }
}
template
<li v-for="item in items">
  {{ item.message }}
</li>

Всередині області v-for, вирази шаблону мають доступ до всіх батьківських властивостей. Крім цього, v-for також підтримує необов'язковий другий псевдонім для індексу поточного елемента масиву:

js
const parentMessage = ref('Батько')
const items = ref([{ message: 'Леся' }, { message: 'Тарас' }])
js
data() {
  return {
    parentMessage: 'Батько',
    items: [{ message: 'Леся' }, { message: 'Тарас' }]
  }
}
template
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
  • Батько - 0 - Леся
  • Батько - 1 - Тарас
  • Область видимості v-for схожа до наступного JavaScript:

    js
    const parentMessage = 'Батько'
    const items = [
      /* ... */
    ]
    
    items.forEach((item, index) => {
      // має доступ до зовнішнього `parentMessage`
      // але `item` та `index` доступні лише тут
      console.log(parentMessage, item.message, index)
    })

    Зверніть увагу на те, як значення для v-for співпадає з сигнатурою колбек-функції для forEach. Насправді ви можете використовувати деструктуризацію псевдоніму елементу в v-for, аналогічно до деструктуризації аргументів функції:

    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: 'Як створювати списки на Vue',
      author: 'Іван Іваненко',
      publishedAt: '2022-08-06'
    })
    js
    data() {
      return {
        myObject: {
          title: 'Як створювати списки на Vue',
          author: 'Іван Іваненко',
          publishedAt: '2022-08-06'
        }
      }
    }
    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 також може приймати число. У такому випадку вона буде повторювати шаблон відповідно до діапазону 1...n кількість разів.

    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. Це означає, що умова 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>

    Підтримка стану за допомогою key

    Коли Vue оновлює список елементів при рендері за допомогою v-for, за замовчуванням використовується стратегія "заміна на місці". Якщо порядок елементів змінився, замість того, щоб переставляти елементи DOM відповідно до нового порядку елементів, Vue замінить кожен елемент на місці, переконавшись, що показується те, що має бути показано за тим чи іншим конкретним індексом.

    Цей режим по замовчуванню є дієвим, але він лише підходить для ситуацій, коли вихідний результат рендеру списку не залежить від стану дочірнього компонента чи тимчасового стану 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. Його не слід плутати зі змінним ключем властивості при використанні v-for з об'єктами.

    Рекомендовано вказувати атрибут key разом з v-for коли це можливо, якщо ітерований вміст DOM не простий (тобто не містить компонентів або елементів DOM зі збереженням стану), або ви навмисно покладаєтеся на поведінку за замовчуванням для підвищення продуктивності.

    Прив'язування атрибута key передбачає примітивні значення, наприклад, рядкові або числові величини. Не використовуйте об'єкти у якості ключів для v-for. Докладніше про використання атрибута key дивіться АРІ документацію для key.

    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. Якщо чітко вказати, звідки надходять дані, цей компонент можна повторно використовувати в інших ситуаціях.

    Перегляньте цей приклад простого списку завдань, щоб побачити, як показувати список компонентів за допомогою v-for, передаючи різні дані до кожного екземпляру.

    Перегляньте цей приклад простого списку завдань, щоб побачити, як показувати список компонентів за допомогою v-for, передаючи різні дані до кожного екземпляру.

    Виявлення зміни масиву

    Методи мутації

    Vue здатний виявляти, коли викликаються методи мутації реактивного масиву, і запускати необхідні оновлення. Цими методами мутації є:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    Заміна масиву

    Методи мутації, як випливає з назви, змінюють вихідний масив, який вони викликають. Для порівняння існують також не мутуючі методи, як-от, filter(), concat() і slice(), які не змінюють вихідний масив, але завжди повертають новий масив. При роботі з не мутуючими методами ми повинні замінити старий масив на новий:

    js
    // `items` є референцією на значення масиву
    items.value = items.value.filter((item) => item.message.match(/Foo/))
    js
    this.items = this.items.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)
    })
    js
    data() {
      return {
        numbers: [1, 2, 3, 4, 5]
      }
    },
    computed: {
      evenNumbers() {
        return this.numbers.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)
    }
    js
    data() {
      return {
        sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
      }
    },
    methods: {
      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()
    Рендеринг списків has loaded