Cours Vue.js · Module 6: Composants
Lifecycle Hooks - Le cycle de vie d'un composant
Dans cette leçon6
Le Dr Nova explique : « Chaque module de la station NOVA LAB passe par des phases : installation, démarrage, mises à jour du firmware, et finalement mise hors service. Les composants Vue ont un cycle de vie identique - et tu peux exécuter du code à chacune de ces phases ! »
Le panneau d'un module doit récupérer des données sur le serveur dès qu'il apparaît à l'écran, et éteindre son compteur de fonctionnement quand il se ferme. Or le code de <script setup> ne s'exécute qu'une seule fois, à la création du composant, avant qu'aucun de ses éléments n'existe dans la page. Il te faut un moyen de dire à Vue : lance ceci quand le panneau est déjà en place, et cela quand il disparaît.
Les phases du cycle de vie d'un composant
Un composant Vue traverse plusieurs phases clés :
- Création - le composant est initialisé, l'état réactif est configuré
- Montage - le composant est inséré dans le DOM (il devient visible sur la page)
- Mise à jour - les données réactives changent, le DOM est mis à jour
- Démontage - le composant est retiré du DOM
À chaque phase, tu peux exécuter du code grâce aux lifecycle hooks - des fonctions spéciales appelées automatiquement par Vue.
Le code écrit directement dans <script setup> correspond à la phase de création : le compilateur transforme ce bloc en une fonction setup(), que Vue appelle une seule fois avant de commencer à monter le composant. C'est aussi là que tu enregistres les hooks - tu appelles une fonction importée du paquet vue et tu lui passes ta propre fonction, que Vue exécutera au bon moment.
onBeforeMount et onMounted
onMounted est le hook le plus utilisé. Il s'exécute après que le composant a été inséré dans le DOM. Compare-le avec onBeforeMount dans un module de statut :
1<template>
2 <div class="module-status">
3 <p>Status: {{ status }}</p>
4 <p>Data: {{ data }}</p>
5 </div>
6</template>
7
8<script setup>
9import { ref, onBeforeMount, onMounted } from 'vue'
10
11const status = ref('Initializing...')
12const data = ref(null)
13
14// Avant le montage - le DOM n'existe pas encore
15onBeforeMount(() => {
16 console.log('Component is about to mount')
17 status.value = 'Mounting...'
18})
19
20// Après le montage - le DOM est prêt !
21onMounted(() => {
22 console.log('Component mounted in DOM')
23 status.value = 'Active'
24
25 // Usage typique : récupérer des données depuis une API
26 fetchModuleData()
27})
28
29async function fetchModuleData() {
30 const response = await fetch('/api/modules')
31 data.value = await response.json()
32}
33</script>L'ordre est toujours le même : d'abord le code de setup, puis onBeforeMount, quand les éléments du composant n'existent pas encore, et enfin onMounted, quand le composant est déjà dans le DOM. Si le composant a des enfants, son onMounted n'est appelé qu'après le montage de tous ses enfants. La fonction fetchModuleData est déclarée plus bas, mais en JavaScript les déclarations de fonctions sont disponibles dans tout le bloc, donc l'appel fonctionne.
Quand utiliser onMounted :
- Récupérer des données depuis une API
- Accéder aux éléments du DOM (
document.querySelector, références) - Initialiser des bibliothèques externes (par ex. graphiques, cartes)
- Démarrer des timers et des intervalles
En Vue, on accède généralement aux éléments du DOM via les template refs, que tu découvriras à l'Atelier des Hologrammes - document.querySelector fonctionne aussi, mais il contourne les mécanismes du framework.
onBeforeUnmount et onUnmounted
Ces hooks s'exécutent quand le composant est retiré du DOM. Ils servent au nettoyage des ressources, par exemple d'un timer démarré au montage :
1<script setup>
2import { ref, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
3
4const timer = ref(null)
5const seconds = ref(0)
6
7onMounted(() => {
8 // Démarre le timer après le montage
9 timer.value = setInterval(() => {
10 seconds.value++
11 }, 1000)
12 console.log('Timer started')
13})
14
15onBeforeUnmount(() => {
16 // Avant le démontage - le composant est encore dans le DOM
17 console.log('Component is about to be removed')
18})
19
20onUnmounted(() => {
21 // Nettoie le timer pour qu'il ne tourne pas après le retrait du composant !
22 if (timer.value) {
23 clearInterval(timer.value)
24 console.log('Timer cleaned up')
25 }
26})
27</script>onBeforeUnmount s'exécute quand le composant est encore sur la page, et onUnmounted quand ses éléments ne sont plus dans le DOM. Ce second endroit est typique pour le nettoyage, car le composant a forcément terminé son travail. Ici, on garde l'identifiant du timer dans un ref(), bien qu'il n'ait pas besoin de réactivité - on ne l'affiche jamais à l'écran.
Pourquoi le nettoyage est-il important ? Si tu ne nettoies pas setInterval, le timer continuera de tourner même après le retrait du composant du DOM, provoquant des fuites de mémoire (memory leaks). Chaque réouverture du panneau ajouterait un compteur de plus, qui modifie des données que plus personne ne regarde.
onBeforeUpdate et onUpdated
Ces hooks réagissent aux changements de données réactives et à la mise à jour du DOM qui les suit :
1<script setup>
2import { ref, onBeforeUpdate, onUpdated } from 'vue'
3
4const count = ref(0)
5
6onBeforeUpdate(() => {
7 // Avant la mise à jour du DOM
8 console.log('DOM is about to update')
9})
10
11onUpdated(() => {
12 // Après la mise à jour du DOM
13 console.log('DOM updated, new value:', count.value)
14})
15</script>onBeforeUpdate voit encore l'ancien DOM, et onUpdated déjà le nouveau. Ne modifie pas l'état du composant dans onUpdated, car chaque changement déclenche un nouveau rendu et risque une boucle infinie. Ces hooks sont moins souvent utilisés - ils servent quand tu dois réagir à des changements du DOM (par ex. position de défilement, animations).
Exemple pratique : un module avec données et timer
Réunissons les hooks les plus importants dans un module de générateur d'oxygène. Le template affiche le nom, le temps de fonctionnement, le statut et un journal d'événements que la directive v-for transforme en liste :
1<template>
2 <div class="station-module" style="background: #0a0e27; color: #00ff88; padding: 1rem;">
3 <h3>Module: {{ moduleName }}</h3>
4 <p>Uptime: {{ uptime }}s</p>
5 <p>Status: {{ status }}</p>
6 <ul>
7 <li v-for="log in logs" :key="log">{{ log }}</li>
8 </ul>
9 </div>
10</template>C'est le script qui fait tout le travail : après le montage, il écrit des entrées dans le journal et démarre le compteur, et après le démontage, il l'arrête :
1<script setup>
2import { ref, onMounted, onUnmounted } from 'vue'
3
4const moduleName = ref('Oxygen Generator')
5const uptime = ref(0)
6const status = ref('Starting...')
7const logs = ref([])
8let intervalId = null
9
10onMounted(() => {
11 logs.value.push('System mounted')
12 status.value = 'Active'
13
14 // Timer qui met à jour uptime chaque seconde
15 intervalId = setInterval(() => {
16 uptime.value++
17 }, 1000)
18
19 logs.value.push('Timer started')
20})
21
22onUnmounted(() => {
23 // Nettoyage au démontage
24 if (intervalId) {
25 clearInterval(intervalId)
26 }
27 console.log('Module shut down, resources released')
28})
29</script>Cette fois, intervalId est un simple let. Tout ce qu'on voit à l'écran vit dans des conteneurs ref(), et tout ce qui ne sert qu'à nettoyer après le module vit dans des variables ordinaires. Je te recommande cette répartition : n'ajoute de la réactivité que là où quelque chose doit se rafraîchir.
L'ordre des hooks - Récapitulatif
Pour finir, tout le cycle au même endroit, dans l'ordre où Vue appelle les différents hooks :
1Création : setup() → onBeforeMount → onMounted
2Mise à jour : onBeforeUpdate → onUpdated
3Démontage : onBeforeUnmount → onUnmountedUne mise à jour peut se répéter de nombreuses fois ou pas du tout, alors que le montage et le démontage n'arrivent qu'une fois dans la vie d'un composant. Ajouter un hook se fait toujours de la même façon : un import depuis vue, un appel avec une fonction, et le code à l'intérieur s'exécute dans la bonne phase.
Retiens les deux plus importants :
onMounted- récupération de données, initialisation (démarrage)onUnmounted- nettoyage des timers et des listeners (cleanup)
Dans l'éditeur ci-dessous, un bouton monte et démonte un composant avec un compteur. Clique dessus et observe l'ordre des hooks dans la console du navigateur.
Un module de station vit exactement comme un composant : ce que tu lances à l'allumage dans onMounted, éteins-le au démontage dans onUnmounted.
Code de cette leçon : App.vue
1<script setup>
2import { ref } from 'vue'
3import LifecycleDemo from './LifecycleDemo.vue'
4
5const showTimer = ref(true)
6</script>
7
8<template>
9 <div class="lifecycle-lab">
10 <div class="header">
11 <h1>Lifecycle Hooks - NOVA LAB</h1>
12 <p class="subtitle">Le cycle de vie d'un composant Vue</p>
13 </div>
14
15 <div class="info-panel">
16 <h3>Lifecycle Hooks</h3>
17 <p><strong>onBeforeMount</strong> - juste avant le montage du composant</p>
18 <p><strong>onMounted</strong> - composant monté, DOM disponible</p>
19 <p><strong>onBeforeUnmount</strong> - juste avant le démontage</p>
20 <p><strong>onUnmounted</strong> - composant démonté, nettoyage des ressources</p>
21 </div>
22
23 <div class="toggle-section">
24 <button class="toggle-btn" @click="showTimer = !showTimer">
25 {{ showTimer ? 'Démonter le composant' : 'Monter le composant' }}
26 </button>
27 <p class="hint">Clique sur le bouton et observe les logs dans la console (F12)</p>
28 </div>
29
30 <LifecycleDemo v-if="showTimer" />
31
32 <div v-if="!showTimer" class="unmounted-msg">
33 <p>Le composant a été démonté.</p>
34 <p>Regarde la console - tu verras les logs onBeforeUnmount et onUnmounted.</p>
35 <p>Le timer a été nettoyé grâce au cleanup dans onUnmounted !</p>
36 </div>
37 </div>
38</template>
39
40<style scoped>
41.lifecycle-lab {
42 background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
43 min-height: 100vh;
44 padding: 2rem;
45 color: #ffffff;
46 font-family: 'Courier New', monospace;
47}
48
49.header {
50 text-align: center;
51 margin-bottom: 2rem;
52 padding-bottom: 1rem;
53 border-bottom: 2px solid #00ff88;
54}
55
56.header h1 {
57 font-size: 2rem;
58 color: #00ff88;
59 text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
60 margin: 0;
61}
62
63.subtitle {
64 color: #00b4d8;
65 font-size: 1.1rem;
66 margin-top: 0.5rem;
67}
68
69.info-panel {
70 background: rgba(0, 180, 216, 0.1);
71 border: 2px solid #00b4d8;
72 border-radius: 10px;
73 padding: 1.5rem;
74 margin-bottom: 2rem;
75}
76
77.info-panel h3 {
78 color: #00b4d8;
79 margin-top: 0;
80}
81
82.info-panel strong {
83 color: #00ff88;
84}
85
86.toggle-section {
87 text-align: center;
88 margin-bottom: 2rem;
89}
90
91.toggle-btn {
92 padding: 0.75rem 2rem;
93 border: 2px solid #ff0055;
94 background: rgba(255, 0, 85, 0.2);
95 color: #ff0055;
96 border-radius: 8px;
97 font-weight: bold;
98 cursor: pointer;
99 font-family: 'Courier New', monospace;
100 font-size: 1rem;
101 transition: all 0.3s ease;
102}
103
104.toggle-btn:hover {
105 background: rgba(255, 0, 85, 0.4);
106 transform: scale(1.05);
107}
108
109.hint {
110 color: #00b4d8;
111 font-size: 0.85rem;
112 margin-top: 0.5rem;
113}
114
115.unmounted-msg {
116 background: rgba(255, 0, 85, 0.1);
117 border: 2px solid #ff0055;
118 border-radius: 10px;
119 padding: 2rem;
120 text-align: center;
121}
122
123.unmounted-msg p {
124 color: #ff0055;
125 margin: 0.5rem 0;
126}
127</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. Que sont les lifecycle hooks en Vue ?
2. Quand onMounted est-il appelé ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Utilise onMounted pour charger des données après le montage du composant.
- Tri vertical
Range les lifecycle hooks dans l'ordre où ils sont appelés :
- Tri vertical
Range les phases du cycle de vie d'un composant Vue dans l'ordre :