Renderowanie Listy
v-for
Możemy użyć dyrektywy v-for
do renderowania listy elementów na podstawie tablicy. Dyrektywa v-for
wymaga specjalnej składni w postaci item in items
, gdzie items
jest tablicą danych źródłowych, a item
jest aliasem dla elementu tablicy, po którym następuje iteracja:
js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="item in items">
{{ item.message }}
</li>
Wewnątrz zakresu v-for
, wyrażenia szablonów mają dostęp do wszystkich właściwości zakresu nadrzędnego. Dodatkowo, v-for
obsługuje także opcjonalny drugi alias dla indeksu bieżącego elementu:
js
const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
Zakres zmiennych w v-for
jest podobny do tego w następującym przykładzie JavaScript:
js
const parentMessage = 'Parent'
const items = [
/* ... */
]
items.forEach((item, index) => {
// ma dostęp do zewnętrznego zakresu `parentMessage`.
// ale `item` i `index` są dostępne tylko w tym miejscu
console.log(parentMessage, item.message, index)
})
Zauważ, że wartość v-for
pasuje do nazwy funkcji wywołania zwrotnego forEach
. W rzeczywistości możesz użyć destrukcji na aliasie elementu v-for
podobnie jak w przypadku destrukturyzacji argumentów funkcji:
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- with index alias -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
Dla zagnieżdżonych v-for
, skalowanie również działa podobnie jak dla zagnieżdżonych funkcji. Każdy zakres v-for
ma dostęp do zakresów nadrzędnych:
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
Możesz także użyć of
jako separatora zamiast in
, tak aby było to bliższe składni JavaScript dla iteracji:
template
<div v-for="item of items"></div>
v-for
z Obiektem
Możesz także użyć v-for
do iteracji po właściwościach obiektu. Kolejność iteracji będzie oparta na wyniku wykonania Object.keys()
na obiekcie:
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>
Można również podać drugi alias dla nazwy właściwości (np. klucz):
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
I jeszcze jeden do indeksu:
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
v-for
z Zakresem
v-for
może również przyjmować liczbę całkowitą. W tym przypadku powtórzy on szablon tyle razy, na podstawie zakresu 1...n
.
template
<span v-for="n in 10">{{ n }}</span>
Zwróć uwagę, że n
zaczyna się od wartości początkowej 1
zamiast 0
.
v-for
na <template>
Podobnie jak w przypadku szablonu v-if
, możesz także użyć znacznika <template>
z v-for
do renderowania bloku wielu elementów. Na przykład:
template
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
z v-if
Note
Nie zaleca się używania v-if
i v-for
na tym samym elemencie z powodu ukrytego pierwszeństwa. Po szczegóły odsyłam do style-guide.
Gdy istnieją na tym samym drzewie, v-if
ma wyższy priorytet niż v-for
. Oznacza to, że warunek v-if
nie będzie miał dostępu do zmiennych z zakresu v-for
:
template
<!--
Spowoduje to wyświetlenie błędu, ponieważ właściwość "todo"
nie jest zdefiniowana w instancji.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
Można to naprawić, przenosząc v-for
do opakowującego znacznika <template>
(który jest też bardziej jednoznaczny):
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
Utrzymywanie stanu za pomocą key
Gdy Vue aktualizuje listę elementów wyrenderowanych za pomocą v-for
, domyślnie używa strategii " łatania w miejscu". Jeśli kolejność elementów danych uległa zmianie, zamiast przesuwać elementy DOM, aby dopasować je do kolejności elementów, Vue poprawi każdy element w miejscu i upewni się, że odzwierciedla on to, co powinno być renderowane w danym indeksie.
Ten domyślny tryb jest wydajny, ale jest odpowiedni tylko wtedy, gdy wynik renderowania listy nie zależy od stanu komponentów potomnych lub tymczasowego stanu DOM (np. wartości wejściowych formularza).
Aby dać Vue wskazówkę, dzięki której będzie ono mogło śledzić tożsamość każdego elementu, a tym samym ponownie wykorzystywać i zmieniać kolejność istniejących elementów, należy podać unikalny atrybut key
dla każdego elementu:
template
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
Podczas używania <template v-for>
, klucz
powinien być umieszczony na szablonie <template>
:
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
Note
key
jest tu specjalnym atrybutem wiązanym za pomocą v-bind
. Nie należy go mylić ze zmienną kluczową właściwości, gdy [używamy v-for
z obiektem] (#v-for-with-an-object).
Zaleca się, aby atrybut v-for
był wyposażony w atrybut key
zawsze, gdy jest to możliwe, chyba że iterowana zawartość DOM jest prosta (tzn. nie zawiera żadnych komponentów lub elementów DOM), lub celowo polegasz na domyślnym zachowaniu by zwiększyć wydajność.
Wiązanie key
oczekuje wartości prymitywnych - tj. strings i numbers. Nie należy używać obiektów jako kluczy v-for
. Dla szczegółowego użycia atrybutu key
, zobacz dokumentację key
API.
v-for
z Componentem
Ta sekcja zakłada znajomość Komponentów. Nie krępuj się pominąć jej i wrócić do niej później.
Możesz bezpośrednio użyć v-for
na komponencie, jak na każdym normalnym elemencie (nie zapomnij podać key
):
template
<MyComponent v-for="item in items" :key="item.id" />
Nie spowoduje to jednak automatycznego przekazania żadnych danych do komponentu, ponieważ komponenty mają własne, izolowane zakresy. Aby przekazać iterowane dane do komponentu, należy również użyć props:
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
Powodem, dla którego nie wstrzykujemy automatycznie item
do komponentu jest to, że czyni to komponent ściśle powiązanym z tym, jak działa v-for
. Wyraźne określenie, skąd pochodzą dane, czyni komponent przydatnym w innych sytuacjach.
Sprawdź ten przykład prostej listy rzeczy do zrobienia to see how to render a list of components using v-for
, passing different data to each instance.
Wykrywanie zmian w tablicy
Metody mutacji
Vue opakowuje metody moderacji obserwowanej tablicy tak, że będą one również wywoływać aktualizacje widoku. Te metody to:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Zamiana tablicy
Metody modyfikujące, jak sama nazwa wskazuje, modyfikują oryginalną tablicę, na której zostały wywołane. Dla porównania, istnieją również metody niemodyfikujące, np. filter()
, concat()
i slice()
, które nie mutują oryginalnej tablicy, ale zawsze zwracają nową tablicę. Podczas pracy z metodami niemutującymi należy zastąpić starą tablicę nową:
js
// `item` jest referencją z wartością tablicową
items.value = items.value.filter((item) => item.message.match(/Foo/))
Można by pomyśleć, że spowoduje to, iż Vue wyrzuci istniejący DOM i ponownie wyrenderuje całą listę - na szczęście tak nie jest. Vue implementuje pewne inteligentne mechanizmy pozwalające na maksymalne ponowne wykorzystanie elementów DOM, więc zastąpienie tablicy inną tablicą zawierającą nakładające się obiekty jest bardzo wydajną operacją.
Wyświetlanie przefiltrowanych/sortowanych wyników
Czasami chcemy wyświetlić przefiltrowaną lub posortowaną wersję tablicy bez faktycznego mutowania lub resetowania oryginalnych danych. W takim przypadku można utworzyć computed property, która zwraca przefiltrowaną lub posortowaną tablicę.
Na przykład:
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>
W sytuacjach, gdy zastosowanie computed properties nie jest możliwe (np. wewnątrz zagnieżdżonych pętli v-for
), można użyć metody:
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>
Bądź ostrożny z reverse()
i sort()
w computed property! Te dwie metody zmutują oryginalną tablicę, co powinno być unikane w computed getterach. Przed wywołaniem tych metod należy utworzyć kopię oryginalnej tablicy:
diff
- return numbers.reverse()
+ return [...numbers].reverse()