1. v-memo
v-memo est une directive qui mémoïse un sous-arbre du template.
Vue saute entiÚrement le re-rendu de l'élément (et de ses enfants) si toutes les valeurs
du tableau de dépendances restent identiques (===) par rapport au dernier rendu.
Syntaxe
<!-- Ne re-rend que si item.id ou selected change -->
<div v-memo="[item.id, selected]">
{{ item.name }} â {{ selected ? 'â' : '' }}
</div>
Cas d'usage typique â liste avec sĂ©lection
<template>
<ul>
<li
v-for="item in list"
:key="item.id"
v-memo="[item.id === selectedId]"
>
<!-- Ce li ne re-rend que si son statut sélectionné change -->
{{ item.name }}
<span v-if="item.id === selectedId">â</span>
</li>
</ul>
</template>
v-memo ne doit pas ĂȘtre utilisĂ© sur des Ă©lĂ©ments ayant v-for Ă la fois (sur le mĂȘme Ă©lĂ©ment) â place v-memo sur l'Ă©lĂ©ment enfant, pas sur celui qui porte v-for.
v-once â encore plus radical
<!-- Rendu UNE SEULE FOIS, jamais mis Ă jour -->
<div v-once>
<h1>{{ title }}</h1>
<p>Contenu statique aprĂšs montage</p>
</div>
Comparaison v-once vs v-memo
| v-once | v-memo | |
|---|---|---|
| Re-rendu | Jamais | Si dépendances changent |
| Flexibilité | Aucune | ContrÎlée |
| Usage | Contenu vraiment statique | Listes partiellement dynamiques |
2. shallowRef / shallowReactive
Par défaut, ref() et reactive() créent une réactivité profonde :
Vue traverse récursivement toutes les propriétés imbriquées pour les rendre réactives.
Pour des objets trÚs grands (centaines de propriétés), ce coût de tracking est évitable.
shallowRef â rĂ©activitĂ© superficielle
import { shallowRef, triggerRef } from 'vue'
// Seule la ref elle-mĂȘme est rĂ©active (pas ses propriĂ©tĂ©s internes)
const bigObject = shallowRef({
data: Array.from({ length: 10000 }, (_, i) => ({ id: i, value: Math.random() }))
})
// Pour forcer une mise Ă jour aprĂšs mutation interne :
bigObject.value.data[0].value = 99
triggerRef(bigObject) // force Vue Ă re-rendre
shallowReactive â objet superficiellement rĂ©actif
import { shallowReactive } from 'vue'
const state = shallowReactive({
count: 0, // réactif
nested: { x: 1 } // NON réactif en profondeur
})
state.count++ // â dĂ©clenche un re-rendu
state.nested.x++ // â ne dĂ©clenche PAS de re-rendu
markRaw â empĂȘcher toute rĂ©activitĂ©
import { reactive, markRaw } from 'vue'
// Cas typique : bibliothĂšque tierce (Chart.js, Three.js...)
import Chart from 'chart.js'
const state = reactive({
chartInstance: markRaw(new Chart(...)), // Vue ne rend pas Chart réactif
count: 0
})
shallowRef / shallowReactive pour les grandes structures de données qui sont remplacées en bloc (pas mutées propriété par propriété). Utilisez markRaw pour les instances de classes tierce comme Three.js, Chart.js.
Comparatif réactivité
| API | Profondeur | Quand l'utiliser |
|---|---|---|
ref() | Profonde | La plupart des cas |
shallowRef() | Surface uniquement | Grands objets remplacés en bloc |
reactive() | Profonde | Objets de state complexes |
shallowReactive() | Surface uniquement | Grands objets peu mis Ă jour |
markRaw() | Aucune | Instances tierces (Chart, Three.js) |
3. defineAsyncComponent
Par défaut, Vue bundle tous les composants ensemble. defineAsyncComponent permet de
charger un composant à la demande (lazy loading), réduisant la taille du bundle initial.
Syntaxe simple
import { defineAsyncComponent } from 'vue'
// Le composant n'est chargé que lorsqu'il est nécessaire
const HeavyChart = defineAsyncComponent(
() => import('./components/HeavyChart.vue')
)
Avec options â loading, error, dĂ©lai
const AsyncChart = defineAsyncComponent({
loader: () => import('./HeavyChart.vue'),
// Composant affiché pendant le chargement
loadingComponent: LoadingSpinner,
// Composant affiché en cas d'erreur
errorComponent: ErrorDisplay,
// Délai avant d'afficher le loading (évite le flash)
delay: 200,
// Timeout avant de passer en erreur
timeout: 5000
})
Avec Suspense (Vue 3)
<template>
<Suspense>
<!-- Contenu async -->
<template #default>
<AsyncUserProfile />
</template>
<!-- Fallback pendant le chargement -->
<template #fallback>
<div>Chargement du profil...</div>
</template>
</Suspense>
</template>
Simuler en CDN (sans build tool)
// En CDN, on simule avec un composant qui se charge aprÚs un délai
const AsyncComponent = defineAsyncComponent(() =>
new Promise(resolve => {
setTimeout(() => {
resolve({
template: '<div>Composant chargé !</div>'
})
}, 1500)
})
)
import() dynamique crée un chunk séparé.
Le navigateur ne le télécharge que quand ce composant est nécessaire.
4. Clés stables dans v-for
L'attribut :key permet à Vue de suivre l'identité des éléments dans une liste.
Un mauvais choix de clé entraßne des re-rendus inutiles, voire des bugs de state.
ProblÚme avec l'index comme clé
<!-- â MAUVAIS : l'index change quand on insĂšre/supprime -->
<li v-for="(item, index) in items" :key="index">
<input v-model="item.value" />
</li>
<!-- Si on insÚre un item au début :
- item[0] avait key=0, maintenant key=0 est un nouvel item
- Vue rĂ©utilise le DOM existant â les inputs ont des valeurs dĂ©calĂ©es ! -->
Solution : ID stable
<!-- â BON : l'ID reste constant mĂȘme si l'ordre change -->
<li v-for="item in items" :key="item.id">
<input v-model="item.value" />
</li>
Générer des IDs uniques
import { ref } from 'vue'
let nextId = 1
function createItem(value) {
return { id: nextId++, value, done: false }
}
const items = ref([
createItem('Apprendre Vue'),
createItem('MaĂźtriser Pinia')
])
Quand l'index est acceptable
- La liste est purement affichée (aucun state interne par item)
- Les items ne sont jamais réordonnés ni filtrés
- La liste est statique
5. Computed vs méthodes dans les templates
Un computed() est mémoïsé : Vue ne le recalcule que si ses dépendances réactives changent.
Une mĂ©thode dans le template est appelĂ©e Ă chaque re-rendu, mĂȘme si rien de pertinent n'a changĂ©.
Exemple â mĂ©thode vs computed
<script setup>
import { ref, computed } from 'vue'
const items = ref([/* 10 000 items */])
const query = ref('')
const unrelated = ref(0) // ne concerne pas le filtre
// â MĂTHODE : recalculĂ©e Ă CHAQUE re-rendu
// Si unrelated change, filteredMethod est rappelée inutilement
function filteredMethod() {
console.log('méthode appelée')
return items.value.filter(i => i.name.includes(query.value))
}
// â COMPUTED : recalculĂ©e SEULEMENT si items ou query change
const filteredComputed = computed(() => {
console.log('computed recalculé')
return items.value.filter(i => i.name.includes(query.value))
})
</script>
<template>
<button @click="unrelated++">Autre action ({{ unrelated }}x)</button>
<input v-model="query" />
<!-- La méthode se relance à chaque click sur le bouton -->
<li v-for="i in filteredMethod()">...</li>
<!-- Le computed reste mémoïsé -->
<li v-for="i in filteredComputed">...</li>
</template>
RĂšgle simple
| Computed | Méthode | |
|---|---|---|
| MĂ©moĂŻsation | Oui â | Non |
| Recalcul | Si dépendances changent | à chaque re-rendu |
| Arguments | Non | Oui |
| Effets de bord | Interdit | Autorisé |
| Idéal pour | Valeurs dérivées coûteuses | Actions, handlers |
computed : pas de fetch, pas de mutation de state, pas de console.log de debug en production. C'est une valeur dérivée pure.
6. Vue DevTools
Vue DevTools est l'outil de débogage officiel de Vue, disponible comme extension Chrome/Firefox ou en tant que plugin Vite. Il offre une inspection en profondeur de l'arbre de composants, du state, des événements et des performances.
Installation
# Extension navigateur
# Chrome : https://chrome.google.com/webstore/detail/vuejs-devtools
# Firefox : https://addons.mozilla.org/fr/firefox/addon/vue-js-devtools/
# Plugin Vite (Vue DevTools v7+)
npm install -D vite-plugin-vue-devtools
Plugin Vite (vite.config.ts)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueDevTools from 'vite-plugin-vue-devtools'
export default defineConfig({
plugins: [
vue(),
VueDevTools() // Ouvre l'UI Ă http://localhost:5173/__devtools__/
]
})
Fonctionnalités clés
| Onglet | Ce qu'on peut faire |
|---|---|
| Components | Inspecter l'arbre, lire/modifier le state en live |
| Timeline | Voir les événements, mutations, hooks dans le temps |
| Pinia | Inspecter les stores, faire du time-travel debugging |
| Router | Historique de navigation, params de route |
| Performance | Profiler les re-rendus, identifier les bottlenecks |
Astuce profiling
// Dans le code, pour mesurer un rendu :
import { onUpdated } from 'vue'
onUpdated(() => {
console.log('Re-rendu Ă ', performance.now().toFixed(2), 'ms')
})