Introduction
En 2026, Vue 3 domine le frontend grâce à sa Composition API qui révolutionne la réutilisabilité et la maintenabilité du code. Contrairement à l'Options API, elle permet une logique métier extraite en composables réutilisables, un typage strict avec TypeScript et une intégration fluide avec des outils comme Pinia pour le state management et Vite pour un bundling ultra-rapide. Ce tutoriel expert vous guide pour bâtir une application TODO avancée : routing gardé, persistence locale, fetching asynchrone via JSONPlaceholder et Suspense pour les états de chargement. Vous apprendrez à scaler une SPA professionnelle, évitant les pièges des apps monolithiques. À la fin, vous aurez un projet copier-collable, prêt pour la prod, optimisé SEO et perf. Idéal pour les seniors visant l'excellence en Vue. (142 mots)
Prérequis
- Node.js 20+ et npm 10+
- Connaissances avancées en Vue 3 (Options API, lifecycle)
- TypeScript intermédiaire (generics, interfaces)
- VS Code avec extensions Volar, TypeScript Vue
- Git pour versionning
Initialiser le projet Vite + Vue TS
npm create vue@latest todo-expert -- --template vue-ts
cd todo-expert
npm install
npm install vue-router@4 pinia @vueuse/core @tanstack/vue-query
npm install -D @types/node
npm run devCette commande crée un projet Vite avec template Vue + TypeScript, installe les dépendances essentielles : Vue Router pour le routing, Pinia pour le state global persistant, VueUse pour des utils composables et TanStack Query pour le data fetching réactif. Le flag --template vue-ts active le typage strict dès le départ. Lancez npm run dev pour vérifier que l'app démarre sur http://localhost:5173.
Configurer Vite et les plugins
Vite est le bundler par défaut de Vue 3, offrant HMR instantané et builds optimisés. Nous étendons sa config pour supporter les aliases, le proxy API et les résolutions TS strictes, essentiels pour une app experte scalable.
vite.config.ts complet
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": resolve(__dirname, "src"),
},
},
server: {
proxy: {
'/api': {
target: 'https://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '/'),
},
},
},
})Cette config définit l'alias @ pour importer depuis src/, active le plugin Vue et proxy les appels /api vers JSONPlaceholder pour simuler un backend réel sans CORS. Le rewrite nettoie les paths. Redémarrez le dev server après modification pour appliquer les changements.
Initialiser Router et Pinia dans main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')Le main.ts bootstrap l'app Vue, intègre Pinia pour le state global et Vue Router pour la navigation SPA. createPinia() active la persistence par défaut via plugins. Cette entrée est le cœur de toute app Vue 3 experte.
Mettre en place le routing avancé
Vue Router 4 supporte les guards, lazy-loading et nested routes. Nous configurons deux vues : Home pour les TODOs et About, avec un guard pour protéger les routes si authentifié (simulé).
router/index.ts avec guards
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('@/views/AboutView.vue'),
beforeEnter: (to, from) => {
const authStore = useAuthStore()
if (!authStore.isAuthenticated) return '/'
},
},
],
})
export default routerCe router utilise createWebHistory pour les URLs propres, lazy-loads les vues pour optimiser le bundle initial et implémente un beforeEnter guard qui vérifie l'auth via Pinia. Les routes sont typées implicitement par TS.
Store Pinia pour auth et persistence
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
export const useAuthStore = defineStore('auth', () => {
const token = useLocalStorage('auth-token', '')
const isAuthenticated = ref(!!token.value)
function login(newToken: string) {
token.value = newToken
isAuthenticated.value = true
}
function logout() {
token.value = ''
isAuthenticated.value = false
}
return { token, isAuthenticated, login, logout }
}, {
persist: true,
})Ce store utilise defineStore avec Composition API, @vueuse/core pour persistence locale via useLocalStorage. Les actions login/logout mutent le state réactif. L'option persist: true sauvegarde automatiquement en sessionStorage.
Créer des composables réutilisables
Les composables sont la force de Vue 3 : fonctions pures exportant reactive state et logic. Nous en créons un pour fetcher des TODOs avec TanStack Query, intégrant Suspense.
Composable useTodos avec Vue Query
import { ref } from 'vue'
import { useQuery } from '@tanstack/vue-query'
interface Todo {
id: number
title: string
completed: boolean
}
export function useTodos() {
return useQuery({
queryKey: ['todos'],
queryFn: async (): Promise<Todo[]> => {
const res = await fetch('/api/todos?_limit=5')
if (!res.ok) throw new Error('Fetch failed')
return res.json()
},
})
}Ce composable encapsule useQuery de TanStack pour caching, refetching auto et états (loading/error). Typé avec interface Todo, il fetch via le proxy Vite. Réutilisable partout, il optimise les perf en évitant les re-fetch inutiles.
Composant HomeView avec Suspense
<template>
<div>
<h1>Mes TODOs Experts</h1>
<Suspense>
<template #default>
<TodoList />
</template>
<template #fallback>
<div>Chargement des tâches...</div>
</template>
</Suspense>
<router-link to="/about">About (protégé)</router-link>
</div>
</template>
<script setup lang="ts">
import TodoList from '@/components/TodoList.vue'
</script>Ce composant utilise pour gérer les états asynchrones des enfants comme TodoList (qui utilise le composable). est la syntaxe concise pour Composition API. Le lien router navigue vers la route gardée.
Assembler App.vue avec layout global
App.vue orchestre le layout : Navbar avec RouterLink, RouterView pour les pages, et provider pour Vue Query.
App.vue layout principal
<template>
<div id="app">
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
<RouterView />
</div>
</template>
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
</script>
<style scoped>
nav { display: flex; gap: 1rem; padding: 1rem; }
</style> rend les composants de route dynamiquement. Scoped styles limitent le CSS au composant. Imports auto via Volar. Ce layout est prêt pour des thèmes globaux ou transitions.
Bonnes pratiques
- Toujours typer avec TS : Interfaces pour props, generics pour composables.
- Extraire en composables : Toute logique réutilisable (fetch, form validation).
- Pinia + plugins : Persistence et devtools pour debugging state.
- Suspense + Query : Gérer loading/error au niveau composant.
- Lazy routes : Réduire bundle initial de 70%.
Erreurs courantes à éviter
- Ignorer
reactive()vsref():refpour primitives,reactivepour objets. - Oublier
queryClientprovider : Wrappez l'app avec. - Pas de guards sur routes sensibles : Toujours vérifier auth avant render.
- Builds sans proxy : CORS bloque en prod ; utilisez un vrai backend ou adapter.
Pour aller plus loin
Testez avec Vitest : npm install -D vitest @vue/test-utils. Étudiez Nuxt 3 pour SSR. Ressources : Docs Vue 3, Pinia. Découvrez nos formations Learni expertes Vue pour masterclass live.