Frameworks

Nuxt 3 : Le Framework Fullstack pour Vue.js
Mark Toledo
3 janvier 2025

Nuxt 3 est le framework fullstack de référence pour Vue.js. Il simplifie le développement d'applications universelles avec le rendu côté serveur (SSR), la génération statique, et une expérience développeur exceptionnelle.
Pourquoi Nuxt ?
Nuxt résout les défis courants du développement Vue.js :
- SEO optimisé grâce au rendu serveur
- Performance avec la génération statique
- DX excellente : auto-imports, routing automatique
- Fullstack : API routes intégrées
- Déploiement flexible : serveur, statique, edge
Installation
npx nuxi@latest init mon-projet-nuxt
cd mon-projet-nuxt
npm install
npm run dev
Structure du projet
mon-projet-nuxt/
├── .nuxt/ # Build (généré)
├── assets/ # Styles, images (transformés par Vite)
├── components/ # Composants Vue (auto-importés)
├── composables/ # Composables (auto-importés)
├── layouts/ # Mises en page
├── middleware/ # Middleware de navigation
├── pages/ # Routes (générées automatiquement)
├── plugins/ # Plugins Vue
├── public/ # Fichiers statiques
├── server/ # API et middleware serveur
├── app.vue # Composant racine
└── nuxt.config.ts # Configuration
Routing automatique
Les fichiers dans pages/ génèrent automatiquement les routes :
pages/
├── index.vue → /
├── about.vue → /about
├── blog/
│ ├── index.vue → /blog
│ └── [slug].vue → /blog/:slug
└── users/
└── [id]/
└── profile.vue → /users/:id/profile
Page basique
<!-- pages/index.vue -->
<template>
<div>
<h1>Accueil</h1>
<NuxtLink to="/about">À propos</NuxtLink>
</div>
</template>
Page dynamique
<!-- pages/blog/[slug].vue -->
<template>
<article>
<h1>{{ article.titre }}</h1>
<div v-html="article.contenu"></div>
</article>
</template>
<script setup>
const route = useRoute()
const { slug } = route.params
// Récupération des données
const { data: article } = await useFetch(`/api/articles/${slug}`)
</script>
Data Fetching
useFetch (recommandé)
<script setup>
// Auto-dédupliqué, cache, SSR-friendly
const { data, pending, error, refresh } = await useFetch('/api/users')
</script>
<template>
<div v-if="pending">Chargement...</div>
<div v-else-if="error">Erreur : {{ error.message }}</div>
<ul v-else>
<li v-for="user in data" :key="user.id">{{ user.nom }}</li>
</ul>
</template>
Options de useFetch
<script setup>
const { data } = await useFetch('/api/products', {
// Clé de cache unique
key: 'products-list',
// Paramètres de requête
query: { category: 'electronics', limit: 10 },
// Méthode et body
method: 'POST',
body: { search: 'laptop' },
// Transformer la réponse
transform: (response) => response.items,
// Exécuter uniquement côté client
server: false,
// Lazy loading (ne bloque pas la navigation)
lazy: true
})
</script>
useAsyncData (contrôle total)
<script setup>
const { data: articles } = await useAsyncData(
'articles-recents',
() => $fetch('/api/articles', { query: { limit: 5 } }),
{
watch: [() => route.query.page] // Re-fetch si la page change
}
)
</script>
API Routes
Créez des endpoints API dans le dossier server/ :
// server/api/users.get.ts
export default defineEventHandler(async (event) => {
return [
{ id: 1, nom: 'Alice' },
{ id: 2, nom: 'Bob' }
]
})
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
// Validation
if (!body.nom || !body.email) {
throw createError({
statusCode: 400,
message: 'Nom et email requis'
})
}
// Création (simulée)
return { id: 3, ...body }
})
// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id')
// Récupération depuis la base de données...
return { id, nom: 'Utilisateur ' + id }
})
Composants auto-importés
Tous les composants dans components/ sont auto-importés :
components/
├── Button.vue → <Button />
├── Header/
│ ├── Logo.vue → <HeaderLogo />
│ └── Nav.vue → <HeaderNav />
└── UI/
├── Card.vue → <UICard />
└── Modal.vue → <UIModal />
Composables
// composables/useAuth.ts
export const useAuth = () => {
const user = useState('user', () => null)
const isLoggedIn = computed(() => !!user.value)
async function login(credentials: { email: string, password: string }) {
const data = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials
})
user.value = data.user
}
function logout() {
user.value = null
navigateTo('/login')
}
return { user, isLoggedIn, login, logout }
}
// Utilisation (auto-importé)
<script setup>
const { user, isLoggedIn, logout } = useAuth()
</script>
Layouts
<!-- layouts/default.vue -->
<template>
<div class="layout">
<Header />
<main>
<slot />
</main>
<Footer />
</div>
</template>
<!-- layouts/admin.vue -->
<template>
<div class="admin-layout">
<Sidebar />
<div class="content">
<slot />
</div>
</div>
</template>
<!-- pages/dashboard.vue -->
<template>
<div>Dashboard admin</div>
</template>
<script setup>
definePageMeta({
layout: 'admin'
})
</script>
Middleware
Middleware de page
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { isLoggedIn } = useAuth()
if (!isLoggedIn.value && to.path !== '/login') {
return navigateTo('/login')
}
})
// Utilisation
<script setup>
definePageMeta({
middleware: 'auth'
})
</script>
Middleware global
// middleware/analytics.global.ts
export default defineNuxtRouteMiddleware((to) => {
// Tracking automatique sur chaque navigation
console.log('Navigation vers:', to.path)
})
SEO et Meta
<script setup>
// Meta basique
useHead({
title: 'Mon titre de page',
meta: [
{ name: 'description', content: 'Description de la page' }
]
})
// SEO avancé
useSeoMeta({
title: 'Article - Mon Blog',
ogTitle: 'Article - Mon Blog',
description: 'Description pour le SEO',
ogDescription: 'Description pour les réseaux sociaux',
ogImage: '/images/article-cover.jpg',
twitterCard: 'summary_large_image'
})
</script>
Configuration
// nuxt.config.ts
export default defineNuxtConfig({
// Modules
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'@nuxt/image'
],
// Variables d'environnement publiques
runtimeConfig: {
// Privé (serveur uniquement)
apiSecret: process.env.API_SECRET,
// Public (client et serveur)
public: {
apiBase: process.env.API_BASE || 'https://api.exemple.fr'
}
},
// SSR ou Static
ssr: true, // ou false pour SPA
// Preset de déploiement
nitro: {
preset: 'vercel' // ou 'netlify', 'cloudflare', etc.
}
})
Déploiement
Génération statique
npm run generate
# Résultat dans .output/public/
Build serveur
npm run build
npm run preview # Test local
# Déployer .output/
Conclusion
Nuxt 3 offre tout ce qu'il faut pour créer des applications Vue.js modernes et performantes. Son approche convention over configuration, les auto-imports et l'intégration fullstack en font un choix excellent pour tout projet, du blog personnel à l'application d'entreprise.
