Passer au contenu

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' }])
js
data() {
  return {
    items: [{ 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' }])
js
data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
template
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
  • Parent - 0 - Foo
  • Parent - 1 - Bar
  • 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.keys() sur l'objet :

    js
    const myObject = reactive({
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    })
    js
    data() {
      return {
        myObject: {
          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 être 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.

    Consultez cet exemple d'une simple todo list pour voir comment rendre une liste de composants en utilisant v-for, 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/))
    js
    this.items = this.items.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 heuristiques 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)
    })
    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>

    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)
    }
    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>

    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 originale avant d'appeler ces méthodes :

    diff
    - return numbers.reverse()
    + return [...numbers].reverse()
    Rendu de listea chargé