Vue.js entre dans une période charnière en 2026 : la version 3.5.31 (mars 2026) consolide la base, et la 3.6 actuellement en bêta introduit le Vapor Mode — une stratégie de compilation qui supprime le DOM virtuel pour générer du code de manipulation directe, avec des gains de rendu de 2 à 4 × sur les composants très dynamiques. Pendant ce temps, la Composition API et la syntaxe script setup se sont imposées comme le standard de fait, l’Options API restant supportée pour la compatibilité. Ce tutoriel construit pas à pas une petite application Vue 3.5 avec routing et store, et termine par un aperçu pratique du Vapor Mode.
Prérequis
- Node.js 22 LTS et un gestionnaire de paquets (pnpm conseillé)
- Un éditeur avec l’extension officielle Vue (Volar) — VS Code recommandé
- Notions de JavaScript ES2022 et de TypeScript de base
- Une heure et demie pour suivre l’ensemble
- Niveau attendu : front-end débutant à intermédiaire
Étape 1 — Comprendre ce que Vue résout en 2026
Vue propose un modèle de composants réactifs déclaratif, comme React, mais avec une approche moins centrée sur le JSX et plus sur les single-file components (un fichier .vue qui regroupe template, script et style). En 2026, ses arguments restent : courbe d’apprentissage plus douce que React, performance comparable, écosystème mature (Vue Router, Pinia, Nuxt), et bientôt Vapor Mode pour des performances de pointe sans changer son code applicatif.
| Critère | Vue 3.5 | React 19 |
|---|---|---|
| Modèle de réactivité | Proxy fin (auto-tracking) | Hooks + signals manuels |
| Single-file components | .vue natifs | JSX dans .tsx |
| State management | Pinia (officiel) | plusieurs (Redux, Zustand…) |
| Server-side rendering | Nuxt 4 | Next.js 15 |
| Compilation | Vite par défaut | Vite ou Webpack |
Le choix entre Vue et React tient à l’équipe et au contexte ; sur un projet neuf en 2026, les deux sont des choix défendables. Vue gagne sur la lisibilité des single-file components et la prévisibilité de la réactivité ; React gagne sur l’écosystème et la disponibilité des développeurs. Les deux convergent — Vue 3.6 va vers Vapor (pas de VDOM), React Compiler optimise désormais automatiquement comme un Vue.
Étape 2 — Créer un projet avec Vite
create-vue est l’outil officiel d’initialisation. Il scaffolde un projet basé sur Vite, avec TypeScript, Vue Router et Pinia activables à la création.
pnpm create vue@latest monapp
# Cocher : TypeScript, Vue Router, Pinia, ESLint
cd monapp
pnpm install
pnpm dev
Le serveur de dev démarre sur le port 5173 en moins de 500 ms grâce à Vite. La structure générée est minimale : src/main.ts bootstrappe l’application, src/App.vue est le composant racine, src/views/ contient les pages, src/components/ les composants réutilisables. Les modifications dans n’importe quel fichier .vue rechargent à chaud sans perdre l’état du composant — l’avantage central de Vite par rapport aux anciens bundlers.
Étape 3 — Premier composant avec script setup
La syntaxe script setup est le sucre syntaxique officiel pour la Composition API : pas de boilerplate, les variables déclarées sont automatiquement exposées au template.
<!-- src/components/Compteur.vue -->
<script setup lang="ts">
import { ref, computed } from "vue";
const count = ref(0);
const doubled = computed(() => count.value * 2);
function increment() {
count.value++;
}
</script>
<template>
<div class="compteur">
<p>Compteur : {{ count }}</p>
<p>Double : {{ doubled }}</p>
<button @click="increment">Incrémenter</button>
</div>
</template>
<style scoped>
.compteur { padding: 16px; border: 1px solid #ddd; border-radius: 8px; }
button { margin-top: 8px; padding: 8px 16px; background: #42b883; color: white; border: 0; border-radius: 4px; }
</style>
Quatre éléments sont à comprendre. ref(0) crée une référence réactive : Vue track automatiquement les lectures et invalide les composants concernés à chaque écriture. La valeur s’accède en JavaScript via .value mais le template Vue déballe automatiquement. computed dérive une valeur en cache qui se recalcule uniquement quand ses dépendances changent — précieux pour éviter de re-calculer à chaque render. style scoped isole les styles au composant, sans avoir à préfixer manuellement les classes.
Étape 4 — La réactivité en pratique
Vue offre trois primitives principales : ref pour les valeurs simples, reactive pour les objets, et watch/watchEffect pour réagir aux changements. Elles sont conçues pour rester invisibles à l’usage tant qu’on suit les règles ; les bugs de réactivité viennent presque toujours d’une déstructuration qui casse le tracking.
import { ref, reactive, watch, watchEffect } from "vue";
const user = reactive({ name: "Aïda", age: 28 });
const loaded = ref(false);
watch(
() => user.name,
(newName, oldName) => console.log(`name : ${oldName} → ${newName}`),
);
watchEffect(() => {
if (loaded.value) console.log(`Bienvenue ${user.name}`);
});
// Anti-pattern : déstructuration qui casse la réactivité
const { name } = user; // ❌ name n'est plus réactif
// Bon pattern
import { toRefs } from "vue";
const { name: r } = toRefs(user); // ✅ r reste réactif
La règle d’or : ne jamais déstructurer un objet reactive directement, toujours passer par toRefs. watch est explicite (on précise la source à observer) ; watchEffect détecte automatiquement les dépendances utilisées dans la fonction. Pour 80 % des cas, watchEffect suffit ; watch est utile quand on a besoin de la valeur précédente ou d’un debounce.
Étape 5 — Routing avec Vue Router
Vue Router 4 est le routeur officiel. Il fonctionne par configuration déclarative et expose des composants RouterLink et RouterView dans les templates.
// src/router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
export default createRouter({
history: createWebHistory(),
routes: [
{ path: "/", name: "home", component: Home },
{ path: "/articles", name: "articles", component: () => import("../views/Articles.vue") },
{ path: "/articles/:id", name: "article", component: () => import("../views/Article.vue") },
],
});
Le import dynamique () => import(...) active le code-splitting automatique : la page Articles n’est téléchargée qu’au moment où l’utilisateur y navigue. Sur une application de cinquante écrans, ce pattern réduit le bundle initial de 70 % en moyenne. Le paramètre :id dans une route est accessible dans le composant cible via useRoute().params.id.
Étape 6 — État global avec Pinia
Pinia est le store officiel depuis Vue 3, en remplacement de Vuex. Il offre une API plus simple, un meilleur typage TypeScript, et un mode setup qui ressemble à un composant Vue.
// src/stores/auth.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
export const useAuth = defineStore("auth", () => {
const user = ref<{ id: number; name: string } | null>(null);
const isLoggedIn = computed(() => user.value !== null);
async function login(email: string, password: string) {
const res = await fetch("/api/login", {
method: "POST", body: JSON.stringify({ email, password }),
headers: { "Content-Type": "application/json" },
});
user.value = await res.json();
}
function logout() { user.value = null; }
return { user, isLoggedIn, login, logout };
});
Le store est utilisable dans n’importe quel composant : const auth = useAuth(); auth.login('a@a.fr', 'mdp'). Pinia gère automatiquement la SSR si vous passez sur Nuxt, et l’extension Vue DevTools affiche en direct l’état du store, l’historique des mutations et permet le time-travel pour le débogage. Pour la persistance (localStorage), le plugin pinia-plugin-persistedstate ajoute la sauvegarde automatique en deux lignes.
Étape 7 — Aperçu du Vapor Mode
Le Vapor Mode (Vue 3.6 bêta en mai 2026) compile certains composants vers du code DOM direct, sans virtual DOM. Le gain typique est de 2 à 4× sur le rendu et de 50 % sur la mémoire, sans changement de code applicatif visible.
<script setup vapor>
import { ref } from "vue";
const items = ref(Array.from({ length: 1000 }, (_, i) => i));
</script>
<template>
<ul>
<li v-for="i in items" :key="i">Item {{ i }}</li>
</ul>
</template>
L’attribut vapor sur le script setup signale au compilateur de basculer ce composant en mode Vapor. Limitations en mai 2026 : seul un sous-ensemble du template est supporté (les directives complexes comme v-show avec transitions doivent rester hors Vapor), et l’Options API n’est pas compatible. Pour la production, attendre la version stable prévue mi à fin 2026 ; pour les expérimentations en interne, le mode est suffisamment stable sur les composants simples très dynamiques (listes, tableaux, formulaires).
Erreurs fréquentes
| Symptôme | Cause | Solution |
|---|---|---|
| Réactivité perdue après déstructuration | Pattern const { x } = reactive(o) |
Utiliser toRefs(o) |
| Erreur « Maximum recursive updates » | watchEffect qui mute sa propre dépendance | Ajouter une condition de sortie |
ref non reconnu dans le template |
Mauvais retour de script setup |
Vérifier que la variable est déclarée au top-level |
| Slot par défaut non rendu | Oubli de <slot /> dans le composant enfant |
Ajouter <slot /> |
| Bundle géant en production | Pas d’import dynamique sur les routes | Lazy-load via () => import(...) |
| Erreur TypeScript sur defineProps | Volar version désuète | Mettre à jour Volar |
Lectures complémentaires
- Documentation officielle vuejs.org
- Vue Router router.vuejs.org
- Pinia pinia.vuejs.org
- Aperçu Vapor Mode core-vapor sur GitHub
- Nuxt 4 nuxt.com