Voici un petit test que j'ai fait pour étudier le re-rendu inutile des nœuds pour les listes dans vue3 (vue2 a le même comportement) : https://kasheftin.github.io/vue3-rerender/ . C'est le code source : https://github.com/Kasheftin/vue3-rerender/tree/master .
J'essaie de comprendre pourquoi Vue rend à nouveau les nœuds déjà rendus dans V-for dans certains cas. Je connais (et fournirai ci-dessous) quelques techniques pour éviter le re-rendu, mais pour moi il est crucial de comprendre la théorie.
Pour les tests, j'ai ajouté une directive v-test factice qui ne fait qu'enregistrer le déclenchement des hooks mounted/beforeUnmount.
Test 1
<div v-for="i in n" :key="i">
<div>{{ i }}</div>
<div v-test="log2">{{ log(i) }}</div>
</div>
Résultat : tous les noeuds sont re-rendus lorsque n augmente. Pourquoi ? Comment éviter cela ?
Test 2
Test2.vue:
<RerenderNumber v-for="i in n" :key="i" :i="i" />
RerenderNumber.vue:
<template>
<div v-test="log2">{{ log() }}</div>
</template>
Résultat : Il fonctionne correctement. Le déplacement du contenu interne de test1 vers un composant distinct résout le problème. Pourquoi ?
Test 3
<RerenderObject v-for="i in n" :key="i" :test="{ i: { i: { i } } }" />
Résultat : un nouveau rendu inutile. Il semble qu'il ne soit pas permis de construire des objets à la volée dans le cycle avant de les envoyer à un composant enfant, probablement pour les raisons suivantes {} != {}
en JavaScript.
Test 4
<template>
<RerenderNumberStore v-for="item in items" :key="item.id" :item="item" />
</template>
<script>
export default {
computed: {
items () {
return this.$store.state.items
}
},
methods: {
addItem () {
this.$store.commit('addItem', { id: this.items.length, name: `Item ${this.items.length}` })
}
}
}
</script>
Ici, le magasin Vuex le plus simple est utilisé. Il fonctionne correctement - pas de re-rendu inutile même si l'élément prop est un objet.
Test 5
<RerenderNumberStore v-for="item in items" :key="item.id" :item="{ id: item.id, name: item.name }" />
Identique au test 4, mais l'élément prop a été restructuré - et nous obtenons un re-rendu inutile.
Test 6
Test6.vue:
<RerenderNumberStoreById v-for="item in items" :key="item.id" :item-id="item.id" />
RerenderNumberStoreById.vue:
<template>
<div v-test="log">{{ item.name }}</div>
</template>
<script>
export default {
props: ['itemId'],
computed: {
item () { return this.$store.state.items.find(item => item.id === this.itemId) }
}
}
</script>
Résultat : un nouveau rendu inutile. Pourquoi ? Je ne peux pas trouver de raison pour laquelle le comportement diffère du test 4. Celui-ci est moins clair pour moi - l'élément calculé n'est pas modifié de quelque façon que ce soit lorsque le nouvel élément est ajouté au tableau des éléments. Il renvoie le MÊME objet. Il doit être mis en cache, correspondre à la valeur précédente et ne déclenche aucune mise à jour dans le DOM.