Rendu de liste
v-for
Nous pouvons utiliser la directive v-for
pour rendre une liste d'items basée sur un tableau. La directive v-for
nécessite une syntaxe spéciale de la forme item in items
, où items
est le tableau de données source et item
est un alias pour l'élément du tableau sur lequel on itère :
js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="item in items">
{{ item.message }}
</li>
À l'intérieur de la portée du v-for
, les expressions du template ont accès à toutes les propriétés de la portée du parent. De plus, v-for
supporte également un second et optionnel alias pour l'index de l'item actuel :
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>
La portée des variables de v-for
est similaire au JavaScript suivant :
js
const parentMessage = 'Parent'
const items = [
/* ... */
]
items.forEach((item, index) => {
// a accès à `parentMessage` de la portée extérieure
// mais `item` et `index` ne sont disponibles qu'ici
console.log(parentMessage, item.message, index)
})
Remarquez comment la valeur de v-for
correspond à la fonction signature du rappel du forEach
. En fait, vous pouvez utiliser la déstructuration sur l'alias item du v-for
de la même manière que la déstructuration des arguments de fonction :
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- avec l'index alias -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
Pour les v-for
imbriqués, les portées fonctionnent de la même manière qu'avec les fonctions imbriquées. Chaque portée de v-for
a accès aux portées de ses parents :
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
Vous pouvez également utiliser of
comme séparateur au lieu de in
, pour que cela soit plus proche de la syntaxe JavaScript pour les itérateurs :
template
<div v-for="item of items"></div>
v-for
avec un objet
Vous pouvez également utiliser v-for
pour itérer sur les propriétés d'un objet. L'ordre d'itération sera basé sur le résultat de l'appel à Object.values()
sur l'objet :
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>
Vous pouvez également fournir un second alias pour le nom de la propriété (aussi appelé clé) :
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
Et un autre pour l'index :
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
v-for
avec une portée
v-for
peut également prendre un nombre entier. Dans ce cas il va répéter le template un certain nombre de fois, basé sur une portée 1...n
.
template
<span v-for="n in 10">{{ n }}</span>
Notez qu'ici n
démarre avec une valeur initiale de 1
au lieu de 0
.
v-for
sur le <template>
Comme le modèle v-if
, vous pouvez aussi utiliser une balise <template>
avec v-for
pour effectuer le rendu d'un bloc composé de plusieurs éléments. Par exemple :
template
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
avec v-if
Note
Il n'est pas recommandé d'utiliser v-if
et v-for
sur le même élément à cause de la préséance implicite. Référez vous aux bonnes pratiques pour plus de détails.
Lorsqu'ils existent sur le même nœud, v-if
a une priorité plus importante que v-for
. Cela signifie que la condition du v-if
n'aura pas accès aux variables de la portée du v-for
:
template
<!--
Une erreur va être levée car la propriété "todo" n'est pas définie sur l'instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
Cela peut être résolu en déplaçant le v-for
sur une balise <template>
enveloppante (ce qui est également plus explicite) :
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
Maintenir l'état avec key
Lorsque Vue met à jour une liste d'éléments rendue avec v-for
, il utilise par défaut une stratégie d'"ajustement sur place". Si l'ordre des données a changé, au lieu de déplacer les éléments du DOM de manière à respecter l'ordre des items, Vue va ajuster chaque élément en place et s'assurer qu'il reflète ce qui devrait être rendu à un index particulier.
Ce mode par défaut est efficace, mais seulement approprié lorsque votre rendu de liste ne dépend pas de l'état d'un composant enfant ou de l'état temporaire du DOM (par exemple les valeurs d'entrées d'un formulaire).
Pour aiguiller Vue afin qu'il puisse tracer l'identité de chaque nœud, et ainsi réutiliser et réarranger l'ordre des éléments existants, vous devez fournir un attribut key
unique pour chaque item :
template
<div v-for="item in items" :key="item.id">
<!-- contenu -->
</div>
Lorsque vous utilisez <template v-for>
, la key
doit être placée sur le conteneur <template>
:
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
Note
Ici, key
est un attribut spécial qui est lié avec v-bind
. Il ne doit pas être confondu avec la variable de clé de propriété utilisée dans le cas d'un v-for
avec un objet.
Il est recommandé de fournir un attribut key
avec v-for
dès que possible, sauf si le contenu du DOM itéré est simple (c'est-à-dire qu'il ne comporte pas de composants ou d'éléments du DOM avec un état), ou si vous comptez intentionnellement sur le comportement par défaut dans un but de gain de performances.
La liaison key
attend des valeurs - c'est-à-dire des chaînes de caractères et des nombres. N'utilisez pas d'objets comme clé de v-for
. Pour l'utilisation détaillée de l'attribut key
, référez vous à la documentation de l'API key
.
v-for
avec un composant
Cette section part du principe que vous connaissez déjà les Composants. N'hésitez pas à la sauter et à revenir plus tard !
Vous pouvez utiliser v-for
directement sur un composant, comme avec n'importe quel élément (n'oubliez pas de fournir une key
):
template
<MyComponent v-for="item in items" :key="item.id" />
Toutefois, les données ne vont pas être automatiquement passées au composant, car les composants ont chacun leur propre portée isolée. Afin de passer la donnée itérée au composant, il faut également utiliser les props :
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
La raison pour laquelle on n'injecte pas automatiquement item
dans le composant est que cela rendrait le composant étroitement lié au fonctionnement de v-for
. Être explicite sur la source d'où proviennent les données rend le composant réutilisable dans d'autres situations.
Regardez cet exemple d'une simple todo list pour comprendre comment rendre une liste de composant en utilsant v-for
, et en passant des données différentes à chaque instance.
Détection des changements pour un tableau
Méthodes de mutation
Vue est capable de détecter quand une méthode de mutation d'un tableau réactif est appelée et d'engendrer les mises à jour nécessaires. Ces méthodes de mutation sont :
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Remplacer un tableau
Les méthodes de mutation, comme leur nom l'indique, vont entrainer des mutations du tableau sur lequel elles sont appelées. En comparaison, il existe également des méthodes non-mutantes, par exemple filter()
, concat()
et slice()
, qui ne modifie pas le tableau original mais retournent toujours un nouveau tableau. Lorsqu'on travaille avec des méthodes non-mutantes, il faut remplacer l'ancien tableau par le nouveau :
js
// `items` est une ref qui a pour valeur un tableau
items.value = items.value.filter((item) => item.message.match(/Foo/))
On pourrait penser que cela va pousser Vue à se débarrasser du DOM actuel et à rendre à nouveau toute la liste - heureusement, ça n'est pas le cas. Vue implémente des approches intelligentes afin de maximiser la réutilisation des éléments du DOM, de manière à ce que remplacer un tableau par un autre tableau composé en partie des mêmes éléments soit une opération très efficace.
Afficher des résultats filtrés/triés
Il arrive parfois que nous voulions afficher une version filtrée ou triée d'un tableau sans muter ou remettre à zéro les données originales. Dans ce cas, on peut créer une propriété calculée qui retourne le tableau filtré ou trié.
Par exemple :
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>
Dans les situations où les propriétés calculées ne sont pas envisageables (par exemple dans des boucles imbriquées v-for
), vous pouvez utiliser une méthode :
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>
Faites attention à reverse()
et sort()
dans une propriété calculée ! Ces deux méthodes vont muter le tableau original, ce qui doit être évité dans des accesseurs calculés. Créez plutôt une copie du tableau original avant d'appeler ces méthodes :
diff
- return numbers.reverse()
+ return [...numbers].reverse()