Cours Vue.js · Module 15: TypeScript et Nuxt
Nuxt 3 - Les bases
Dans cette leçon8
Nuxt 3 est un méta-framework construit sur Vue 3 qui fournit le SSR, le SSG, le routage basé sur les fichiers et bien plus encore - clé en main, sans configuration. Vois-le comme le système de pilote automatique de la station NOVA LAB - tout est configuré et prêt à fonctionner.
Créer un projet Nuxt 3
1npx nuxi@latest init nova-lab-app
2cd nova-lab-app
3npm install
4npm run devLe routage basé sur les fichiers
Dans Nuxt 3, tu ne configures pas le router manuellement. La structure des fichiers du dossier pages/ crée automatiquement les routes :
1pages/
2├── index.vue → /
3├── about.vue → /about
4├── crew/
5│ ├── index.vue → /crew
6│ └── [id].vue → /crew/123
7└── modules/
8 └── [...slug].vue → /modules/any/nested/pathLe fichier index.vue est la page principale d'un répertoire donné. Les fichiers entre crochets [id].vue sont des paramètres dynamiques.
Les layouts
Les layouts définissent la structure partagée des pages (navigation, pied de page) :
1<!-- layouts/default.vue -->
2<template>
3 <div class="station">
4 <nav class="nav">
5 <NuxtLink to="/">Home</NuxtLink>
6 <NuxtLink to="/about">About</NuxtLink>
7 <NuxtLink to="/crew">Crew</NuxtLink>
8 </nav>
9
10 <main>
11 <slot />
12 </main>
13
14 <footer>NOVA LAB © 2087</footer>
15 </div>
16</template>Les pages utilisent automatiquement le layout default. Tu peux changer le layout sur une page précise :
1<!-- pages/dashboard.vue -->
2<script setup lang="ts">
3definePageMeta({
4 layout: 'admin'
5})
6</script>useFetch - Récupération de données
useFetch est un composable Nuxt pour récupérer des données. Il fonctionne à la fois sur le serveur (SSR) et sur le client :
1<script setup lang="ts">
2interface CrewMember {
3 id: number
4 name: string
5 role: string
6}
7
8// useFetch fonctionne côté serveur et côté client
9const { data: crew, pending, error } = await useFetch<CrewMember[]>(
10 '/api/crew'
11)
12</script>
13
14<template>
15 <div v-if="pending">Loading...</div>
16 <div v-else-if="error">Error: {{ error.message }}</div>
17 <ul v-else>
18 <li v-for="member in crew" :key="member.id">
19 {{ member.name }} - {{ member.role }}
20 </li>
21 </ul>
22</template>useFetch fait automatiquement :
- L'exécution de la requête sur le serveur pendant le SSR
- La sérialisation des données et leur envoi au client (évite le double fetch)
- La fourniture des états
pending,error,data,refresh
useAsyncData - Plus flexible
useAsyncData donne un contrôle total sur la logique de récupération des données :
1<script setup lang="ts">
2const { data: stats } = await useAsyncData('station-stats', () => {
3 return $fetch('/api/stats')
4})
5
6// Avec transformation des données
7const { data: modules } = await useAsyncData(
8 'modules',
9 () => $fetch('/api/modules'),
10 {
11 transform: (data) => data.filter(m => m.active),
12 watch: [selectedSector] // rafraîchissement automatique
13 }
14)
15</script>Auto-imports
Nuxt 3 importe automatiquement :
- Les composables Vue (
ref,computed,watch...) - Les composables Nuxt (
useFetch,useRoute,useState...) - Les composants du dossier
components/ - Les utilitaires de
utils/etcomposables/
Pas besoin d'écrire import { ref } from 'vue' - Nuxt le fait pour nous !
1<script setup lang="ts">
2// Aucun import ! Nuxt importe automatiquement ref, computed, useFetch
3const count = ref(0)
4const doubled = computed(() => count.value * 2)
5
6const { data } = await useFetch('/api/data')
7</script>Structure d'un projet Nuxt 3
1nova-lab-app/
2├── pages/ # Routage basé sur les fichiers
3├── components/ # Composants auto-importés
4├── composables/ # Composables auto-importés
5├── layouts/ # Layouts des pages
6├── server/ # Endpoints API (Nitro)
7│ └── api/ # Routes /api/*
8├── public/ # Fichiers statiques
9├── app.vue # Composant racine
10└── nuxt.config.ts # Configuration de NuxtServer API (Nitro)
Nuxt 3 utilise Nitro pour gérer les endpoints API :
1// server/api/crew.ts
2export default defineEventHandler((event) => {
3 return [
4 { id: 1, name: 'Dr. Nova', role: 'Commander' },
5 { id: 2, name: 'Atlas', role: 'Navigator' },
6 { id: 3, name: 'Echo', role: 'Engineer' }
7 ]
8})Code de cette leçon : App.vue
1<script setup lang="ts">
2import { ref } from 'vue'
3
4// Simulation de la structure Nuxt 3
5// (Sandpack ne prend pas en charge Nuxt, on simule donc le concept)
6
7type Page = 'home' | 'crew' | 'modules' | 'crew-detail'
8
9const currentPage = ref<Page>('home')
10const selectedCrewId = ref<number | null>(null)
11
12// Simulation de useFetch
13interface CrewMember {
14 id: number
15 name: string
16 role: string
17}
18
19interface StationModule {
20 id: string
21 name: string
22 status: string
23}
24
25// Données "depuis l'API"
26const crewData: CrewMember[] = [
27 { id: 1, name: 'Dr. Nova', role: 'Commander' },
28 { id: 2, name: 'Atlas', role: 'Navigator' },
29 { id: 3, name: 'Echo', role: 'Engineer' },
30 { id: 4, name: 'Vega', role: 'Scientist' }
31]
32
33const modulesData: StationModule[] = [
34 { id: 'm1', name: 'Life Support', status: 'online' },
35 { id: 'm2', name: 'Navigation', status: 'online' },
36 { id: 'm3', name: 'Science Lab', status: 'maintenance' }
37]
38
39// Simulation de l'état loading
40const pending = ref(false)
41
42function navigate(page: Page, id?: number) {
43 pending.value = true
44 setTimeout(() => {
45 currentPage.value = page
46 if (id) selectedCrewId.value = id
47 pending.value = false
48 }, 300)
49}
50
51function getCrewMember(id: number) {
52 return crewData.find(c => c.id === id)
53}
54</script>
55
56<template>
57 <div class="nova-lab">
58 <nav class="nav">
59 <button @click="navigate('home')" :class="{ active: currentPage === 'home' }">Home</button>
60 <button @click="navigate('crew')" :class="{ active: currentPage === 'crew' }">Crew</button>
61 <button @click="navigate('modules')" :class="{ active: currentPage === 'modules' }">Modules</button>
62 </nav>
63
64 <div v-if="pending" class="loading">Loading...</div>
65
66 <div v-else>
67 <!-- pages/index.vue -->
68 <div v-if="currentPage === 'home'" class="page">
69 <h1>NOVA LAB - Nuxt 3 Demo</h1>
70 <p>Simulation du file-based routing et de useFetch</p>
71 <div class="info-box">
72 <p><strong>pages/</strong> index.vue -> /</p>
73 <p><strong>pages/</strong> crew/index.vue -> /crew</p>
74 <p><strong>pages/</strong> crew/[id].vue -> /crew/:id</p>
75 <p><strong>pages/</strong> modules.vue -> /modules</p>
76 </div>
77 </div>
78
79 <!-- pages/crew/index.vue -->
80 <div v-if="currentPage === 'crew'" class="page">
81 <h1>Crew</h1>
82 <p class="note">Data fetched via useFetch('/api/crew')</p>
83 <div
84 v-for="member in crewData"
85 :key="member.id"
86 class="card clickable"
87 @click="navigate('crew-detail', member.id)"
88 >
89 <strong>{{ member.name }}</strong>
90 <span>{{ member.role }}</span>
91 </div>
92 </div>
93
94 <!-- pages/crew/[id].vue -->
95 <div v-if="currentPage === 'crew-detail' && selectedCrewId" class="page">
96 <button @click="navigate('crew')" class="back-btn">Retour</button>
97 <h1>{{ getCrewMember(selectedCrewId)?.name }}</h1>
98 <p>Rôle : {{ getCrewMember(selectedCrewId)?.role }}</p>
99 <p class="note">Route: /crew/{{ selectedCrewId }}</p>
100 </div>
101
102 <!-- pages/modules.vue -->
103 <div v-if="currentPage === 'modules'" class="page">
104 <h1>Station modules</h1>
105 <p class="note">useAsyncData('modules', () => $fetch('/api/modules'))</p>
106 <div v-for="mod in modulesData" :key="mod.id" class="card">
107 <strong>{{ mod.name }}</strong>
108 <span :class="mod.status">{{ mod.status }}</span>
109 </div>
110 </div>
111 </div>
112 </div>
113</template>
114
115<style scoped>
116.nova-lab {
117 background: #0a0e27;
118 color: #00ff88;
119 min-height: 100vh;
120 padding: 20px;
121 font-family: monospace;
122}
123h1 { color: #00ff88; margin-bottom: 10px; }
124.nav {
125 display: flex;
126 gap: 8px;
127 margin-bottom: 15px;
128 border-bottom: 1px solid rgba(0,180,216,0.3);
129 padding-bottom: 12px;
130}
131.nav button {
132 background: rgba(0,180,216,0.1);
133 color: #00b4d8;
134 border: 1px solid #00b4d8;
135 padding: 8px 18px;
136 border-radius: 6px;
137 cursor: pointer;
138 font-family: monospace;
139}
140.nav button.active { background: #00b4d8; color: white; }
141.page { padding: 10px 0; }
142.card {
143 display: flex;
144 justify-content: space-between;
145 background: rgba(0,180,216,0.08);
146 border: 1px solid #00b4d8;
147 padding: 12px 16px;
148 margin: 6px 0;
149 border-radius: 8px;
150}
151.card.clickable { cursor: pointer; }
152.card.clickable:hover { background: rgba(0,180,216,0.15); }
153.note { color: #666; font-size: 12px; margin-bottom: 10px; }
154.info-box {
155 background: rgba(0,255,136,0.05);
156 border: 1px solid rgba(0,255,136,0.2);
157 padding: 14px;
158 border-radius: 8px;
159 margin-top: 10px;
160 font-size: 13px;
161}
162.info-box p { margin: 4px 0; color: #ccc; }
163.loading { color: #f39c12; text-align: center; padding: 40px; }
164.back-btn {
165 background: none;
166 color: #00b4d8;
167 border: 1px solid #00b4d8;
168 padding: 6px 14px;
169 border-radius: 6px;
170 cursor: pointer;
171 margin-bottom: 10px;
172 font-family: monospace;
173}
174.online { color: #00ff88; }
175.maintenance { color: #f39c12; }
176.offline { color: #e74c3c; }
177</style>Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Comment Nuxt 3 crée-t-il le routage de l'application ?
2. Dans quel dossier Nuxt 3 place-t-il les fichiers qui deviennent automatiquement des routes de l'application ?
Voici 2 des 6 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Utilise onMounted pour accéder à window - un objet indisponible sur le serveur.
- Tri horizontal
Ordonne le chemin de fichier d'une route dynamique /crew/123 dans Nuxt :
- Tri vertical
Ordonne les étapes de l'Incremental Static Regeneration :
- Clique dans l'ordre
Ordonne les états de useFetch selon un flux typique :
- Tri horizontal
Ordonne la structure d'un layout Nuxt avec le composant NuxtLink :
- Tri vertical
Ordonne les stratégies du meilleur SEO au plus faible :
- Clique dans l'ordre
Ordonne les commandes pour créer et lancer un projet Nuxt 3 :
- Tri vertical
Ordonne les concepts TypeScript dans Vue du plus simple au plus avancé :
- Tri vertical
Ordonne le cycle de vie des données récupérées par useFetch en SSR Nuxt :
- Tri horizontal
Ordonne le nom du fichier de configuration principal de Nuxt 3 :
- Éditeur de code
Définis les interfaces StationModule et Alert, type props, emits et computed.