Cours Vue.js · Module 10: Vue Router

Routes imbriquées

2 min de lecture
Dans cette leçon5

La base NOVA LAB a des modules, les modules ont des systèmes, les systèmes ont des composants - c'est une hiérarchie. De la même manière, on peut imbriquer des routes, créant des mises en page complexes avec des sous-pages.

Configuration des routes imbriquées

La clé est la propriété children dans la définition de la route :

1const routes = [
2  {
3    path: '/mission-control',
4    name: 'mission-control',
5    component: MissionControlLayout,
6    children: [
7      {
8        path: '',  // vue par défaut pour /mission-control
9        name: 'mission-overview',
10        component: MissionOverview
11      },
12      {
13        path: 'systems',  // /mission-control/systems
14        name: 'mission-systems',
15        component: MissionSystems
16      },
17      {
18        path: 'crew',  // /mission-control/crew
19        name: 'mission-crew',
20        component: MissionCrew
21      }
22    ]
23  }
24]

Important : les chemins des children SANS slash au début sont relatifs au parent. Ainsi 'systems' devient '/mission-control/systems'.

Composant parent avec RouterView

Le composant parent DOIT contenir <RouterView> - c'est là que se rendent les children :

1<!-- MissionControlLayout.vue -->
2<template>
3  <div class="layout">
4    <aside class="sidebar">
5      <h2>Centre de contrôle</h2>
6      <nav>
7        <RouterLink :to="{ name: 'mission-overview' }">
8          Aperçu
9        </RouterLink>
10        <RouterLink :to="{ name: 'mission-systems' }">
11          Systèmes
12        </RouterLink>
13        <RouterLink :to="{ name: 'mission-crew' }">
14          Équipage
15        </RouterLink>
16      </nav>
17    </aside>
18
19    <main class="content">
20      <!-- Les routes imbriquées se rendent ici -->
21      <RouterView />
22    </main>
23  </div>
24</template>

Route enfant par défaut

Un path: '' vide définit la vue par défaut affichée lorsque l'utilisateur navigue vers le chemin parent :

1children: [
2  {
3    path: '',  // /mission-control
4    component: MissionOverview
5  },
6  {
7    path: 'systems',  // /mission-control/systems
8    component: MissionSystems
9  }
10]

Redirection

Tu peux aussi utiliser une redirection à la place d'une vue par défaut :

1children: [
2  {
3    path: '',
4    redirect: '/mission-control/dashboard'
5  },
6  {
7    path: 'dashboard',
8    component: Dashboard
9  }
10]

Vues nommées (named views)

Une seule route peut rendre plusieurs composants dans différents emplacements <RouterView> :

1const routes = [
2  {
3    path: '/mission-control',
4    components: {
5      default: MissionMain,
6      sidebar: MissionSidebar,
7      footer: MissionFooter
8    }
9  }
10]
1<template>
2  <RouterView name="sidebar" />
3  <RouterView /> <!-- default -->
4  <RouterView name="footer" />
5</template>
Code de cette leçon : App.vue
1<script setup>
2import { ref } from 'vue'
3
4// Simulation des navigation guards
5const isAuthenticated = ref(false)
6const currentRoute = ref('home')
7const guardLog = ref([])
8const pageTitle = ref('NOVA LAB - Home')
9
10const routes = [
11  { name: 'home', label: 'Home', meta: {} },
12  { name: 'login', label: 'Login', meta: { requiresGuest: true } },
13  { name: 'dashboard', label: 'Dashboard', meta: { requiresAuth: true } },
14  { name: 'admin', label: 'Admin', meta: { requiresAuth: true, requiresAdmin: true } }
15]
16
17function navigate(routeName) {
18  const route = routes.find(r => r.name === routeName)
19  if (!route) return
20
21  guardLog.value = []
22
23  // Simulation de beforeEach
24  guardLog.value.push('beforeEach: ' + currentRoute.value + ' -> ' + routeName)
25
26  // Vérifie requiresAuth
27  if (route.meta.requiresAuth && !isAuthenticated.value) {
28    guardLog.value.push('BLOCKED: requiresAuth - redirect to login')
29    currentRoute.value = 'login'
30    pageTitle.value = 'NOVA LAB - Login'
31    return
32  }
33
34  // Vérifie requiresGuest
35  if (route.meta.requiresGuest && isAuthenticated.value) {
36    guardLog.value.push('BLOCKED: requiresGuest - redirect to home')
37    currentRoute.value = 'home'
38    pageTitle.value = 'NOVA LAB - Home'
39    return
40  }
41
42  // Navigation autorisée
43  guardLog.value.push('ALLOWED: navigation ok')
44  currentRoute.value = routeName
45  pageTitle.value = 'NOVA LAB - ' + route.label
46
47  // Simulation de afterEach
48  guardLog.value.push('afterEach: title = ' + pageTitle.value)
49}
50
51function toggleAuth() {
52  isAuthenticated.value = !isAuthenticated.value
53  guardLog.value.push('Auth: ' + (isAuthenticated.value ? 'connecté' : 'déconnecté'))
54}
55</script>
56
57<template>
58  <div class="nova-lab">
59    <h1>NOVA LAB - Navigation Guards</h1>
60
61    <div class="auth-bar">
62      <span>Statut : {{ isAuthenticated ? 'Connecté' : 'Non connecté' }}</span>
63      <button @click="toggleAuth" class="auth-btn">
64        {{ isAuthenticated ? 'Déconnexion' : 'Connexion' }}
65      </button>
66    </div>
67
68    <nav class="main-nav">
69      <button
70        v-for="route in routes"
71        :key="route.name"
72        @click="navigate(route.name)"
73        :class="{ active: currentRoute === route.name }"
74        class="nav-btn"
75      >
76        {{ route.label }}
77        <span v-if="route.meta.requiresAuth" class="badge lock">LOCK</span>
78        <span v-if="route.meta.requiresGuest" class="badge guest">GUEST</span>
79      </button>
80    </nav>
81
82    <div class="guard-log">
83      <h3>Guard Log:</h3>
84      <div v-for="(log, i) in guardLog" :key="i" class="log-entry"
85        :class="{
86          blocked: log.includes('BLOCKED'),
87          allowed: log.includes('ALLOWED')
88        }"
89      >
90        {{ log }}
91      </div>
92    </div>
93
94    <div class="page-content">
95      <h2>{{ pageTitle }}</h2>
96      <pre>
97// Configuration des guards :
98router.beforeEach((to, from) => {
99  if (to.meta.requiresAuth && !isAuthenticated) {
100    return { name: 'login' }
101  }
102})
103
104router.afterEach((to) => {
105  document.title = to.meta.title || 'NOVA LAB'
106})</pre>
107    </div>
108  </div>
109</template>
110
111<style scoped>
112.nova-lab {
113  background: #0a0e27;
114  color: #00ff88;
115  min-height: 100vh;
116  padding: 20px;
117  font-family: monospace;
118}
119h1 { color: #00ffff; margin-bottom: 10px; }
120h2 { color: #00b4d8; }
121h3 { color: #00ffff; margin-bottom: 5px; }
122.auth-bar {
123  display: flex;
124  align-items: center;
125  gap: 10px;
126  padding: 8px 12px;
127  background: #1a1e37;
128  margin-bottom: 10px;
129  color: #c0c0c0;
130}
131.auth-btn {
132  background: rgba(0, 255, 136, 0.2);
133  border: 1px solid #00ff88;
134  color: #00ff88;
135  padding: 4px 10px;
136  cursor: pointer;
137  font-family: monospace;
138}
139.main-nav { display: flex; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
140.nav-btn {
141  background: rgba(0, 180, 216, 0.1);
142  border: 1px solid #00b4d8;
143  color: #00ff88;
144  padding: 6px 12px;
145  cursor: pointer;
146  font-family: monospace;
147  position: relative;
148}
149.nav-btn.active {
150  background: #00b4d8;
151  color: #0a0e27;
152}
153.badge {
154  font-size: 9px;
155  padding: 1px 4px;
156  margin-left: 4px;
157}
158.badge.lock { color: #ff4444; }
159.badge.guest { color: #ffaa00; }
160.guard-log {
161  background: #1a1e37;
162  padding: 10px;
163  margin-bottom: 10px;
164  max-height: 120px;
165  overflow-y: auto;
166}
167.log-entry {
168  font-size: 12px;
169  color: #888;
170  padding: 2px 0;
171}
172.log-entry.blocked { color: #ff4444; }
173.log-entry.allowed { color: #00ff88; }
174.page-content {
175  background: rgba(0, 180, 216, 0.05);
176  border: 1px solid #1a1e37;
177  padding: 15px;
178}
179pre {
180  background: #1a1e37;
181  padding: 10px;
182  color: #00ff88;
183  font-size: 11px;
184  margin-top: 10px;
185}
186</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. 1. Comment définit-on des routes imbriquées dans Vue Router ?

  2. 2. Où sont rendus les composants des routes imbriquées ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Configure une route parente avec un tableau children et une mise en page avec <RouterView>.

  • Tri vertical

    Ordonne les éléments du plus externe au plus interne :

  • Clique dans l'ordre

    Ordonne le code de définition d'une route imbriquée :

  • Éditeur de code

    Ajoute une route enfant par défaut avec un path vide et un composant overview.