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>
Attention : 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-oncev-memo
Re-renduJamaisSi dépendances changent
FlexibilitéAucuneContrÎlée
UsageContenu vraiment statiqueListes 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
})
RÚgle pratique : utilisez 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é

APIProfondeurQuand l'utiliser
ref()ProfondeLa plupart des cas
shallowRef()Surface uniquementGrands objets remplacés en bloc
reactive()ProfondeObjets de state complexes
shallowReactive()Surface uniquementGrands objets peu mis Ă  jour
markRaw()AucuneInstances 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)
  })
)
En production avec Vite, chaque 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

L'index comme clé est acceptable uniquement si :
  • 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

ComputedMéthode
MĂ©moĂŻsationOui ✓Non
RecalculSi dĂ©pendances changentÀ chaque re-rendu
ArgumentsNonOui
Effets de bordInterditAutorisé
Idéal pourValeurs dérivées coûteusesActions, handlers
Ne jamais faire d'effets de bord dans un 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

OngletCe qu'on peut faire
ComponentsInspecter l'arbre, lire/modifier le state en live
TimelineVoir les événements, mutations, hooks dans le temps
PiniaInspecter les stores, faire du time-travel debugging
RouterHistorique de navigation, params de route
PerformanceProfiler 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')
})
Vue DevTools détecte automatiquement Vue 3 sur la page. Pour les projets CDN, l'extension fonctionne directement. Pour les projets Vite, le plugin offre des fonctionnalités supplémentaires comme l'inspection du code source et la navigation vers les fichiers.