Performances Vue 3 en 2026 : Vapor Mode, Alien Signals et la fin du Virtual DOM

Plongée dans les performances du Vapor Mode de Vue 3.6 : comment il élimine le Virtual DOM, le système de réactivité Alien Signals, les benchmarks face à Solid.js et les techniques d'optimisation concrètes pour la production.

Schéma d'optimisation des performances du Vapor Mode de Vue 3 montrant des mises à jour DOM directes sans Virtual DOM

Le Vapor Mode de Vue 3.6 représente le changement d'architecture de rendu le plus important depuis l'adoption du Virtual DOM par Vue dans sa version 2. En compilant les Single File Components directement en opérations DOM impératives, le Vapor Mode élimine le surcoût de comparaison qui définit le pipeline de rendu de Vue depuis des années. Associé à la réécriture du système de réactivité Alien Signals, Vue 3.6 atteint la parité avec Solid.js et Svelte 5 sur les benchmarks, sans obliger les développeurs à apprendre une nouvelle API.

Le Vapor Mode en bref

Le Vapor Mode est une stratégie de compilation optionnelle introduite dans Vue 3.6 qui contourne entièrement le Virtual DOM. Les composants compilés en Vapor Mode relient chaque dépendance réactive directement au nœud DOM exact qu'elle affecte, produisant des mises à jour chirurgicales sans aucun parcours d'arbre. Son activation tient en un seul attribut : <script setup vapor>.

Le fonctionnement du Virtual DOM de Vue : pourquoi il est devenu un goulot d'étranglement

Le patron Virtual DOM (VDOM), popularisé par React et adopté par Vue 2, crée une représentation JavaScript légère de l'arbre DOM réel. À chaque changement d'état, Vue génère un nouvel arbre VDOM, le compare au précédent et n'applique au DOM réel que les nœuds modifiés.

Cette approche fonctionne bien pour la plupart des applications. L'algorithme de comparaison s'exécute en temps O(n), et le compilateur de Vue exclut déjà les sous-arbres statiques du chemin de comparaison. Mais le surcoût s'accumule dans certains scénarios :

  • Les grandes listes de centaines de lignes déclenchent des comparaisons complètes de sous-arbres à chaque mise à jour
  • Les changements d'état fréquents (animations, données temps réel) génèrent un brassage de VDOM que le ramasse-miettes doit nettoyer
  • Les arbres de composants profonds multiplient le coût du parcours d'arbre et de la génération des patchs

Le compilateur de templates de Vue 3 a introduit plusieurs optimisations du VDOM (hoisting statique, patch flags, block trees) qui ont réduit le travail inutile. Elles ont apporté des améliorations mesurables, mais l'architecture fondamentale exigeait toujours de générer, comparer puis jeter des objets JavaScript à chaque cycle de rendu.

Vue 3.6 Vapor Mode : compiler pour supprimer le Virtual DOM

Le Vapor Mode adopte une approche radicalement différente. Au lieu de compiler les templates en fonctions de rendu qui retournent des nœuds VDOM, le compilateur Vapor génère du code qui crée et met à jour directement les éléments DOM. Chaque liaison réactive correspond à une mutation DOM précise, sans aucune représentation intermédiaire.

Voici comment un composant Vue standard se compile différemment selon le mode :

vue
<!-- Counter.vue -->
<script setup vapor>
import { ref } from 'vue'

const count = ref(0)
const increment = () => count.value++
</script>

<template>
  <button @click="increment">
    Count: {{ count }}
  </button>
</template>

En mode VDOM classique, ce template se compile en une fonction de rendu qui retourne un arbre de nœuds virtuels. À chaque clic, Vue crée un nouvel arbre VDOM, le compare au précédent, détecte le changement du contenu textuel et applique le patch au DOM réel.

En Vapor Mode, le compilateur génère un code proche de ceci :

javascript
// Sortie de compilation Vapor (simplifiée)
const button = document.createElement('button')
const text = document.createTextNode('Count: 0')
button.appendChild(text)

// Liaison directe : source réactive -> mutation DOM
effect(() => {
  text.nodeValue = `Count: ${count.value}`
})

button.addEventListener('click', increment)

L'effet réactif relie count directement à text.nodeValue. Aucune création de VDOM, aucune comparaison, aucun patch. Le changement d'état déclenche exactement une mutation DOM.

Activer le Vapor Mode dans un projet

Le Vapor Mode fonctionne au niveau du composant. Deux stratégies d'intégration existent :

vaporApp.js — Application 100 % Vapor (sans runtime VDOM)javascript
import { createVaporApp } from 'vue'
import App from './App.vue'

createVaporApp(App).mount('#app')
hybridApp.js — Composants VDOM + Vapor mélangésjavascript
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'

createApp(App)
  .use(vaporInteropPlugin)
  .mount('#app')

L'approche hybride permet une migration progressive. Les composants critiques en performance, tableaux de données, tableaux de bord temps réel, vues riches en animations, peuvent adopter Vapor pendant que le reste de l'application continue d'utiliser le runtime VDOM standard. Les deux types de composants coexistent dans le même arbre.

Limites du Vapor Mode dans Vue 3.6

Le Vapor Mode requiert la Composition API avec <script setup>. L'Options API, app.config.globalProperties, getCurrentInstance() et les événements de cycle de vie par élément (@vue:mounted, etc.) ne sont pas pris en charge. Les directives personnalisées utilisent une interface différente qui accepte des getters réactifs au lieu d'objets de binding. Suspense fonctionne lorsqu'il enveloppe des composants Vapor à l'intérieur d'un parent VDOM, mais pas dans une application 100 % Vapor.

Alien Signals : le nouveau moteur de réactivité de Vue

Vue 3.6 introduit un second changement majeur aux côtés du Vapor Mode : une réécriture complète de @vue/reactivity fondée sur la bibliothèque Alien Signals. Créée par Johnson Chu, Alien Signals implémente un algorithme de réactivité push-pull qui réduit à la fois l'allocation mémoire et le coût de calcul.

Le modèle push-pull fonctionne en deux phases :

  1. Phase push : lorsqu'un signal (source réactive) change, il propage un drapeau « dirty » à toutes les propriétés calculées dépendantes. Ce parcours est peu coûteux : il ne fait que basculer des drapeaux booléens sans exécuter aucun calcul.
  2. Phase pull : lorsqu'une propriété calculée est lue, elle vérifie son drapeau dirty. Si elle est dirty, elle recalcule. Si elle est propre, elle retourne immédiatement la valeur en cache.

Cette conception élimine les recalculs inutiles. Dans le système de réactivité de Vue 3.5, une propriété calculée dépendant de trois signaux se recalculait dès qu'un signal changeait, même si le résultat restait identique. Alien Signals ne recalcule que lorsque la valeur est effectivement lue et qu'au moins une dépendance a changé.

reactivityComparison.tstypescript
import { ref, computed, watch } from 'vue'

const firstName = ref('Jane')
const lastName = ref('Doe')
const isActive = ref(true)

// Ce computed ne recalcule que s'il est lu ET dirty
const displayName = computed(() => {
  return isActive.value
    ? `${firstName.value} ${lastName.value}`
    : 'Inactive User'
})

// Modifier isActive marque displayName comme dirty
// Mais aucun calcul ne s'exécute tant que rien ne lit displayName.value
isActive.value = false

// MAINTENANT le recalcul a lieu
console.log(displayName.value) // 'Inactive User'

La structure de données interne a également changé. Alien Signals remplace le suivi des dépendances fondé sur Set par des listes doublement chaînées, réduisant la mémoire par dépendance réactive et accélérant le parcours lors du nettoyage.

Résultats des benchmarks : Alien Signals à l'épreuve

Les chiffres des benchmarks de la bêta Vue 3.6 montrent des améliorations constantes par rapport à Vue 3.5 :

| Métrique | Vue 3.5 | Vue 3.6 (Alien Signals) | Amélioration | |---|---|---|---| | Mémoire par ref réactive | Référence | -14 % | Allocation réduite | | Recalcul des computed | Référence | -20 % en moyenne | Moins d'exécutions inutiles | | Montage de composants (100k) | ~300 ms | ~100 ms | 3x plus rapide | | First Contentful Paint | Référence | 0,7 s en général | Surcoût du framework réduit | | Taille du bundle de base | ~16 Ko | <10 Ko | -37 % |

Ces gains s'appliquent automatiquement à toutes les applications Vue 3.6. Contrairement au Vapor Mode, qui demande une adoption par composant, le système de réactivité Alien Signals est activé par défaut dans Vue 3.6.

Prêt à réussir tes entretiens Vue.js / Nuxt.js ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Techniques concrètes d'optimisation des performances Vue 3

Le Vapor Mode et Alien Signals s'attaquent au surcoût du framework, mais les performances au niveau applicatif dépendent toujours de la structure des composants. Ces techniques s'appliquent aux composants VDOM comme Vapor.

Réactivité superficielle pour les grandes structures de données

La réactivité profonde enveloppe chaque propriété imbriquée dans un Proxy. Pour de grands jeux de données, réponses d'API, objets de configuration, état mis en cache, cela crée des milliers de wrappers Proxy inutiles. shallowRef et shallowReactive limitent la réactivité à la référence de premier niveau.

shallowReactivity.tstypescript
import { shallowRef, triggerRef } from 'vue'

interface Product {
  id: number
  name: string
  variants: { sku: string; price: number }[]
}

// Seule la ref elle-même est réactive, pas les propriétés imbriquées
const products = shallowRef<Product[]>([])

// Récupération des données : assigner le nouveau tableau déclenche la réactivité
async function fetchProducts() {
  const response = await fetch('/api/products')
  products.value = await response.json() // déclenche la mise à jour
}

// Mutation de données imbriquées : déclenchement manuel obligatoire
function updatePrice(productId: number, sku: string, newPrice: number) {
  const product = products.value.find(p => p.id === productId)
  const variant = product?.variants.find(v => v.sku === sku)
  if (variant) {
    variant.price = newPrice
    triggerRef(products) // déclenchement explicite requis
  }
}

La directive v-memo pour le rendu de listes coûteuses

Pour les composants VDOM qui rendent de longues listes, v-memo met en cache le résultat du rendu d'un sous-arbre en fonction de valeurs de dépendance. Le VDOM saute entièrement la comparaison des sous-arbres mémoïsés dont les dépendances n'ont pas changé.

vue
<!-- ProductGrid.vue -->
<script setup>
import { ref } from 'vue'

const products = ref([])
const selectedId = ref(null)
</script>

<template>
  <div class="grid">
    <div
      v-for="product in products"
      :key="product.id"
      v-memo="[product.id === selectedId, product.price]"
      :class="{ selected: product.id === selectedId }"
    >
      <h3>{{ product.name }}</h3>
      <span>{{ product.price }}</span>
    </div>
  </div>
</template>

Le tableau v-memo [product.id === selectedId, product.price] indique à Vue : ne rends à nouveau cet élément que si l'état de sélection ou le prix ont changé. Pour une liste de 500 produits où un seul est sélectionné, cela réduit le travail VDOM de 500 comparaisons de sous-arbres à 2 (l'élément précédemment sélectionné et le nouveau).

Composants asynchrones avec Suspense pour le découpage du code

Charger paresseusement les composants lourds garde le bundle initial léger. Le defineAsyncComponent de Vue associé à Suspense gère l'état de chargement de façon déclarative.

vue
<!-- Dashboard.vue -->
<script setup>
import { defineAsyncComponent } from 'vue'

// Bibliothèque de graphiques lourde chargée uniquement au besoin
const AnalyticsChart = defineAsyncComponent(() =>
  import('./components/AnalyticsChart.vue')
)

const DataExport = defineAsyncComponent({
  loader: () => import('./components/DataExport.vue'),
  delay: 200,       // affiche le chargement après 200 ms
  timeout: 10000,   // échoue après 10 s
})
</script>

<template>
  <Suspense>
    <template #default>
      <AnalyticsChart />
      <DataExport />
    </template>
    <template #fallback>
      <div class="skeleton-loader" />
    </template>
  </Suspense>
</template>

Vapor Mode vs VDOM : quand utiliser chaque approche

Le Vapor Mode n'est pas un remplaçant universel du Virtual DOM. Chaque mode de compilation a des forces adaptées à différents profils de composants.

| Scénario | Mode recommandé | Raison | |---|---|---| | Tableaux de données (1000+ lignes) | Vapor | Élimine le surcoût VDOM par ligne | | Tableaux de bord temps réel | Vapor | Les mises à jour fréquentes profitent de la liaison DOM directe | | Composants riches en animations | Vapor | Aucune pression GC liée au brassage VDOM | | Bibliothèques de composants tierces en VDOM | VDOM | La couche d'interop ajoute de la complexité | | Composants utilisant l'Options API | VDOM | Vapor exige la Composition API | | Formulaires à validation complexe | Indifférent | Surcoût de rendu minime dans les deux modes | | Pages de contenu statique | Indifférent | Le SSG/SSR fait le gros du travail |

Le chemin de migration recommandé : profiler d'abord l'application avec l'onglet Performance de Vue DevTools. Repérer les composants au temps de rendu et à la fréquence de re-rendu les plus élevés. Les convertir en Vapor Mode, mesurer l'impact, puis étendre à partir de là.

Questions d'entretien : performances Vue 3 et Vapor Mode

Ces questions reflètent ce que les équipes d'ingénierie demandent pour évaluer l'expertise Vue en 2026. Chaque réponse résume le raisonnement technique attendu par un recruteur.

Q : Quel problème le Vapor Mode résout-il, et en quoi diffère-t-il des optimisations VDOM que Vue possédait déjà ?

Le compilateur VDOM de Vue 3 optimisait déjà les sous-arbres statiques, ajoutait des patch flags et implémentait des block trees pour sauter les comparaisons inutiles. Cela réduisait le surcoût VDOM sans l'éliminer : chaque changement d'état exigeait toujours de créer des nœuds VDOM, de parcourir l'arbre et de générer des patchs. Le Vapor Mode supprime tout ce pipeline. Le compilateur fait correspondre l'état réactif directement aux mutations DOM, si bien qu'un changement d'état déclenche exactement les opérations DOM nécessaires : aucune structure de données intermédiaire, aucun algorithme de comparaison, aucun ramassage de nœuds VDOM jetés.

Q : Les composants Vapor et VDOM peuvent-ils coexister dans la même application ?

Oui. Le vaporInteropPlugin autorise les deux types de composants dans un même arbre. Un parent VDOM peut rendre des enfants Vapor et inversement, avec quelques réserves : les slots Vapor ne peuvent pas utiliser slots.default() à l'intérieur d'un composant VDOM (utiliser renderSlot à la place), et l'interopérabilité avec les bibliothèques de composants en VDOM (Vuetify, PrimeVue) peut présenter des aspérités pendant la phase expérimentale.

Q : Expliquez le modèle de réactivité push-pull des Alien Signals dans Vue 3.6.

Le modèle push-pull divise les mises à jour réactives en deux phases. Dans la phase push, lorsqu'un signal change de valeur, le système propage un drapeau dirty en aval vers toutes les propriétés calculées dépendantes : c'est peu coûteux puisqu'il ne fait que basculer des drapeaux booléens. Dans la phase pull, lorsqu'une valeur calculée est réellement lue, elle vérifie si elle est dirty. Si oui, elle recalcule à partir de ses dépendances. Sinon, elle retourne la valeur en cache. Cela évite de recalculer trop tôt des propriétés calculées qui pourraient ne jamais être lues durant un cycle de mise à jour donné.

Q : Quand faut-il utiliser shallowRef plutôt que ref dans une application Vue 3 ?

shallowRef convient lorsque la structure de données est grande et que seule la réassignation de premier niveau doit déclencher la réactivité : caches de réponses d'API, objets de configuration et grands tableaux où les mutations d'éléments individuels sont gérées manuellement avec triggerRef(). La réactivité profonde enveloppe chaque propriété imbriquée dans un Proxy, un surcoût inutile pour des données qui seront remplacées en bloc plutôt que mutées sur place.

Pour s'entraîner davantage, consultez les questions d'entretien Vue.js sur les composables et les patrons de réactivité sur SharpSkill.

Pour aller plus loin

Le guide de performance officiel de Vue.js couvre d'autres techniques d'optimisation, notamment la stabilité des props, le défilement virtuel et le streaming SSR. Les notes de version de la bêta Vue 3.6 documentent chaque API du Vapor Mode et ses limitations connues.

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Conclusion

  • Le Vapor Mode compile les SFC Vue en opérations DOM directes, éliminant entièrement le surcoût de création, de comparaison et de patch du Virtual DOM
  • Il s'active par composant avec <script setup vapor>, sans migration à l'échelle de l'application
  • Alien Signals, le nouveau moteur de réactivité par défaut, réduit l'usage mémoire de 14 % et améliore les performances des propriétés calculées grâce à l'évaluation paresseuse push-pull
  • Utiliser shallowRef pour les grandes structures de données, v-memo pour le rendu de listes coûteuses et defineAsyncComponent pour le découpage du code : ces patrons valent dans les deux modes, VDOM et Vapor
  • Le Vapor Mode requiert la Composition API avec <script setup>. L'Options API, getCurrentInstance() et globalProperties ne sont pas pris en charge
  • Profiler d'abord avec Vue DevTools, convertir les composants à fort impact en Vapor, mesurer, puis itérer
  • Vue 3.6 est en bêta début 2026 : traiter le Vapor Mode comme expérimental pour la production, mais commencer dès maintenant à monter l'équipe en compétence

Tags

#vue
#vue-3
#vapor-mode
#performance
#virtual-dom
#alien-signals
#reactivity

Partager

Articles similaires