Vue.js course Β· Module 6: Components
Lifecycle Hooks - Component Lifecycle
In this lesson6
Dr. Nova explains: "Every NOVA LAB station module goes through phases: installation, startup, firmware updates, and eventually decommissioning. Vue components have an identical lifecycle - and you can execute code in each of these phases!"
A module panel should fetch data from the server right after it appears on the screen, and switch off its uptime counter when it closes. But the code in <script setup> runs only once, when the component is created, before any of its elements exist on the page. You need a way to tell Vue: run this when the panel is already standing, and this when it disappears.
Component Lifecycle Phases
A Vue component goes through several key phases:
- Creation - the component is initialized, reactive state is configured
- Mounting - the component is inserted into the DOM (becomes visible on the page)
- Updating - reactive data changes, DOM is updated
- Unmounting - the component is removed from the DOM
In each phase you can run code using lifecycle hooks - special functions called automatically by Vue.
Code written directly in <script setup> is the creation phase: the compiler turns this block into a setup() function, which Vue calls once before it starts mounting the component. That's also where you register hooks - you call a function imported from the vue package and pass it your own function, which Vue will run at the right moment.
onBeforeMount and onMounted
onMounted is the most commonly used hook. It runs after the component has been inserted into the DOM. Compare it with onBeforeMount in a status module:
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// Before mounting - DOM doesn't exist yet
15onBeforeMount(() => {
16 console.log('Component is about to mount')
17 status.value = 'Mounting...'
18})
19
20// After mounting - DOM is ready!
21onMounted(() => {
22 console.log('Component mounted in DOM')
23 status.value = 'Active'
24
25 // Typical use: fetching data from API
26 fetchModuleData()
27})
28
29async function fetchModuleData() {
30 const response = await fetch('/api/modules')
31 data.value = await response.json()
32}
33</script>The order is always the same: first the setup code, then onBeforeMount, when the component's elements don't exist yet, and finally onMounted, when the component is already in the DOM. If the component has children, its onMounted is called only after all the children have been mounted. The fetchModuleData function is declared further down, but function declarations in JavaScript are available in the whole block, so the call works.
When to use onMounted:
- Fetching data from API
- Accessing DOM elements (
document.querySelector, references) - Initializing external libraries (e.g., charts, maps)
- Starting timers and intervals
In Vue you usually reach DOM elements through template refs, which you'll learn in the Hologram Workshop - document.querySelector works too, but it bypasses the framework's mechanisms.
onBeforeUnmount and onUnmounted
These hooks run when the component is being removed from the DOM. They serve for resource cleanup, for example of a timer started during mounting:
1<script setup>
2import { ref, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
3
4const timer = ref(null)
5const seconds = ref(0)
6
7onMounted(() => {
8 // Start timer after mounting
9 timer.value = setInterval(() => {
10 seconds.value++
11 }, 1000)
12 console.log('Timer started')
13})
14
15onBeforeUnmount(() => {
16 // Before unmounting - component is still in DOM
17 console.log('Component is about to be removed')
18})
19
20onUnmounted(() => {
21 // Clean up timer so it doesn't run after component removal!
22 if (timer.value) {
23 clearInterval(timer.value)
24 console.log('Timer cleaned up')
25 }
26})
27</script>onBeforeUnmount runs while the component is still on the page, and onUnmounted when its elements are no longer in the DOM. The latter is the typical place for cleanup, because the component has definitely finished its work. We keep the timer identifier in a ref() here, although it doesn't need reactivity - we never display it on the screen.
Why is cleanup important? If you don't clear setInterval, the timer will keep running even after the component is removed from DOM, causing memory leaks. Every reopening of the panel would add another counter that changes data nobody is looking at anymore.
onBeforeUpdate and onUpdated
These hooks react to reactive data changes and the DOM update that follows them:
1<script setup>
2import { ref, onBeforeUpdate, onUpdated } from 'vue'
3
4const count = ref(0)
5
6onBeforeUpdate(() => {
7 // Before DOM update
8 console.log('DOM is about to update')
9})
10
11onUpdated(() => {
12 // After DOM update
13 console.log('DOM updated, new value:', count.value)
14})
15</script>onBeforeUpdate still sees the old DOM, and onUpdated already sees the new one. Don't change the component's state in onUpdated, because every change triggers another re-render and risks an infinite loop. These hooks are used less frequently - they're useful when you need to react to DOM changes (e.g., scroll position, animations).
Practical Example: Module with Data and Timer
Let's put the most important hooks together in an oxygen generator module. The template displays the name, the uptime, the status and an event log that the v-for directive turns into a list:
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>The script does all the work: after mounting it writes entries to the log and starts the counter, and after unmounting it stops it:
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 updating uptime every second
15 intervalId = setInterval(() => {
16 uptime.value++
17 }, 1000)
18
19 logs.value.push('Timer started')
20})
21
22onUnmounted(() => {
23 // Cleanup on unmount
24 if (intervalId) {
25 clearInterval(intervalId)
26 }
27 console.log('Module shut down, resources released')
28})
29</script>This time intervalId is a plain let. Everything you can see on the screen lives in ref() containers, and whatever only cleans up after the module lives in ordinary variables. I recommend this split to you - add reactivity only where something needs to refresh.
Hook Order - Summary
Finally, the whole cycle in one place, in the order in which Vue calls the individual hooks:
1Creation: setup() β onBeforeMount β onMounted
2Update: onBeforeUpdate β onUpdated
3Unmounting: onBeforeUnmount β onUnmountedAn update can repeat many times or not at all, while mounting and unmounting happen once in a component's life. Adding a hook always looks the same: an import from vue, a call with a function, and the code inside it runs in the right phase.
Remember the two most important ones:
onMounted- fetching data, initialization (start)onUnmounted- cleaning up timers, listeners (cleanup)
In the editor below, a button mounts and unmounts a component with a counter. Click it and watch the order of the hooks in the browser console.
A station module lives just like a component: whatever you start at ignition in onMounted, switch off at decommissioning in onUnmounted.
Code for this lesson: 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">Vue component lifecycle</p>
13 </div>
14
15 <div class="info-panel">
16 <h3>Lifecycle Hooks</h3>
17 <p><strong>onBeforeMount</strong> - just before the component is mounted</p>
18 <p><strong>onMounted</strong> - component mounted, DOM available</p>
19 <p><strong>onBeforeUnmount</strong> - just before unmounting</p>
20 <p><strong>onUnmounted</strong> - component unmounted, cleaning up resources</p>
21 </div>
22
23 <div class="toggle-section">
24 <button class="toggle-btn" @click="showTimer = !showTimer">
25 {{ showTimer ? 'Unmount component' : 'Mount component' }}
26 </button>
27 <p class="hint">Click the button and watch the logs in the console (F12)</p>
28 </div>
29
30 <LifecycleDemo v-if="showTimer" />
31
32 <div v-if="!showTimer" class="unmounted-msg">
33 <p>The component has been unmounted.</p>
34 <p>Check the console - you will see onBeforeUnmount and onUnmounted logs.</p>
35 <p>The timer was cleared thanks to cleanup in 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>Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What are lifecycle hooks in Vue?
2. When is onMounted called?
These are 2 of 3 questions for this lesson. Solve the rest in the game.
Hands-on tasks in the game
- Code editor
Use onMounted to load data after the component is mounted.
- Vertical ordering
Arrange the lifecycle hooks in the order they are called:
- Vertical ordering
Arrange the Vue component lifecycle phases in order: