🎭 Vue.js Lifecycle Hooks: The Story of a Component’s Life
The Big Picture 🌟
Imagine a butterfly. It goes through amazing stages: egg → caterpillar → cocoon → beautiful butterfly → eventually says goodbye.
Your Vue component is just like that butterfly! It has its own life stages, and at each stage, you can do special things. These special moments are called Lifecycle Hooks.
🦋 The Four Life Stages
graph TD A["🥚 BIRTH - Mount Hooks"] --> B["🐛 LIVING - Update Hooks"] B --> C["🦋 GOODBYE - Unmount Hooks"] D["🛡️ ERROR GUARD - onErrorCaptured"] D -.-> A D -.-> B D -.-> C
| Stage | What Happens | Real Life Example |
|---|---|---|
| Mount | Component is born | Baby opens eyes |
| Update | Component changes | Kid grows taller |
| Unmount | Component leaves | Saying goodbye |
| Error | Something goes wrong | Doctor catches illness |
🥚 Mount Lifecycle Hooks
When: Your component is being born and placed on the screen.
Think of it like a new student joining a classroom:
- First, the student gets ready at home (setup)
- Then, the student walks into the classroom (mount)
onBeforeMount
What: Runs RIGHT BEFORE your component appears on screen.
Analogy: The student is standing at the door, about to walk in. The desk isn’t assigned yet!
import { onBeforeMount } from 'vue'
onBeforeMount(() => {
console.log('About to appear!')
// ⚠️ Can't touch the screen yet
// The DOM doesn't exist
})
When to Use onBeforeMount:
- ✅ Setting up data before display
- ✅ Preparing things that don’t need the screen
- ❌ NOT for touching buttons or text on screen
onMounted
What: Runs AFTER your component is on the screen.
Analogy: The student has sat down at their desk. Now they can write on the board!
import { onMounted } from 'vue'
onMounted(() => {
console.log('I am on screen!')
// ✅ NOW you can touch the DOM
const button = document.querySelector('button')
button.focus()
})
Real Example: Fetching Data
import { ref, onMounted } from 'vue'
const users = ref([])
const loading = ref(true)
onMounted(async () => {
// Fetch data when component appears
const response = await fetch('/api/users')
users.value = await response.json()
loading.value = false
})
When to Use onMounted:
- ✅ Fetching data from a server
- ✅ Setting up timers or intervals
- ✅ Connecting to external libraries (charts, maps)
- ✅ Focusing on input fields
🎯 Mount Hooks Summary
graph TD A["Component Created"] --> B["onBeforeMount"] B --> C["Template Renders"] C --> D["onMounted"] D --> E["✨ Component Ready!"] style B fill:#FFE4B5 style D fill:#90EE90
| Hook | DOM Available? | Use Case |
|---|---|---|
| onBeforeMount | ❌ No | Setup data, no screen touch |
| onMounted | ✅ Yes | Fetch data, use libraries |
🐛 Update Lifecycle Hooks
When: Your component’s data changes and it needs to redraw.
Think of it like a whiteboard being erased and rewritten:
- Teacher says “I’m about to change the board”
- Teacher erases and writes new stuff
- “Done! Look at the new board!”
onBeforeUpdate
What: Runs RIGHT BEFORE the screen updates.
Analogy: The teacher raised the eraser but hasn’t erased yet.
import { onBeforeUpdate } from 'vue'
onBeforeUpdate(() => {
console.log('About to change!')
// The OLD content is still on screen
// Perfect for saving scroll position
})
Real Example: Save Scroll Position
import { ref, onBeforeUpdate } from 'vue'
const savedScrollTop = ref(0)
onBeforeUpdate(() => {
// Save where user was scrolling
const chatBox = document.querySelector('.chat')
if (chatBox) {
savedScrollTop.value = chatBox.scrollTop
}
})
onUpdated
What: Runs AFTER the screen finishes updating.
Analogy: The teacher finished writing. New content is visible!
import { onUpdated } from 'vue'
onUpdated(() => {
console.log('Screen updated!')
// The NEW content is now on screen
})
Real Example: Restore Scroll Position
import { ref, onUpdated } from 'vue'
onUpdated(() => {
// Keep user at same scroll position
const chatBox = document.querySelector('.chat')
if (chatBox) {
chatBox.scrollTop = savedScrollTop.value
}
})
⚠️ Warning: Infinite Loop Danger!
// 🚫 DON'T DO THIS!
onUpdated(() => {
count.value++ // This triggers another update!
// Creates infinite loop!
})
// ✅ DO THIS instead
onUpdated(() => {
// Only READ, don't change reactive data
console.log('Count is now:', count.value)
})
🎯 Update Hooks Summary
graph TD A["Data Changes"] --> B["onBeforeUpdate"] B --> C["Screen Redraws"] C --> D["onUpdated"] D --> E["✨ Update Complete!"] style B fill:#FFE4B5 style D fill:#90EE90
| Hook | What You See | Best For |
|---|---|---|
| onBeforeUpdate | OLD screen | Save state |
| onUpdated | NEW screen | React to changes |
🦋 Unmount Lifecycle Hooks
When: Your component is leaving the screen forever.
Think of it like cleaning up your room before moving out:
- “I’m about to leave” (pack boxes)
- “Goodbye, room!” (door closes)
onBeforeUnmount
What: Runs RIGHT BEFORE component is removed.
Analogy: You’re still in the room, packing your bags.
import { onBeforeUnmount } from 'vue'
onBeforeUnmount(() => {
console.log('Cleaning up...')
// Stop timers, close connections
// Component is STILL on screen
})
onUnmounted
What: Runs AFTER component is completely gone.
Analogy: You’ve left the building. The room is empty.
import { onUnmounted } from 'vue'
onUnmounted(() => {
console.log('Component is gone!')
// Final cleanup
// Component is NO LONGER on screen
})
🧹 Why Cleanup Matters
Imagine you turn on a faucet (timer) but forget to turn it off. Water floods everywhere!
Real Example: Timer Cleanup
import { ref, onMounted, onUnmounted } from 'vue'
const seconds = ref(0)
let timer = null
onMounted(() => {
// Start a timer
timer = setInterval(() => {
seconds.value++
}, 1000)
})
onUnmounted(() => {
// STOP the timer!
// If we forget, it keeps running
// even after component is gone!
clearInterval(timer)
})
Real Example: Event Listener Cleanup
import { onMounted, onUnmounted } from 'vue'
const handleResize = () => {
console.log('Window resized!')
}
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
// Remove listener!
window.removeEventListener('resize', handleResize)
})
🎯 Unmount Hooks Summary
graph TD A["Time to Leave"] --> B["onBeforeUnmount"] B --> C["Component Removed"] C --> D["onUnmounted"] D --> E["💨 Gone Forever!"] style B fill:#FFE4B5 style D fill:#FFB6C1
| Hook | Component Status | Use Case |
|---|---|---|
| onBeforeUnmount | Still visible | Start cleanup |
| onUnmounted | Gone | Final cleanup |
What to Clean Up:
- ⏱️ Timers (setInterval, setTimeout)
- 📡 Event listeners
- 🔌 WebSocket connections
- 📊 Third-party library instances
🛡️ onErrorCaptured
What: Catches errors from child components.
Think of it like a safety net at a circus. If a trapeze artist (child component) falls, the net (parent) catches them!
graph TD A["Parent Component"] --> B["Child Component"] B --> C["Grandchild Component"] C -->|Error!| D["💥 Crash!"] D -->|Caught by| A style A fill:#90EE90 style D fill:#FF6B6B
Basic Usage
import { onErrorCaptured, ref } from 'vue'
const error = ref(null)
onErrorCaptured((err, instance, info) => {
// err = the error object
// instance = component that broke
// info = what was happening
error.value = err.message
console.log('Caught error:', err)
// Return false to STOP error
// from going higher up
return false
})
The Three Parameters
| Parameter | What It Is | Example |
|---|---|---|
err |
The error itself | Error: Cannot read property |
instance |
Broken component | The child that crashed |
info |
What was happening | "mounted hook" |
Real Example: Error Boundary
<template>
<div v-if="hasError" class="error-box">
😱 Something broke!
<button @click="retry">Try Again</button>
</div>
<slot v-else></slot>
</template>
<script setup>
import { ref, onErrorCaptured } from 'vue'
const hasError = ref(false)
onErrorCaptured((err) => {
hasError.value = true
console.error('Child error:', err)
return false // Stop propagation
})
const retry = () => {
hasError.value = false
}
</script>
When to Use onErrorCaptured:
- ✅ Creating “Error Boundary” components
- ✅ Logging errors to a server
- ✅ Showing user-friendly error messages
- ✅ Preventing entire app from crashing
🎬 The Complete Picture
graph TD subgraph Mount A["onBeforeMount"] --> B["onMounted"] end subgraph Update C["onBeforeUpdate"] --> D["onUpdated"] end subgraph Unmount E["onBeforeUnmount"] --> F["onUnmounted"] end B --> C D --> C D --> E G["onErrorCaptured"] -.-> A G -.-> C G -.-> E style B fill:#90EE90 style D fill:#87CEEB style F fill:#FFB6C1 style G fill:#DDA0DD
🚀 Quick Reference Table
| Hook | When | DOM? | Common Use |
|---|---|---|---|
onBeforeMount |
Before appearing | ❌ | Pre-setup |
onMounted |
After appearing | ✅ | Fetch data, init libraries |
onBeforeUpdate |
Before redraw | OLD | Save scroll position |
onUpdated |
After redraw | NEW | React to changes |
onBeforeUnmount |
Before leaving | ✅ | Start cleanup |
onUnmounted |
After leaving | ❌ | Final cleanup |
onErrorCaptured |
Child error | ✅ | Error handling |
💡 Pro Tips
1. Always Clean Up!
If you start something in onMounted, stop it in onUnmounted.
2. Don’t Change Data in onUpdated
Reading is fine. Changing reactive data causes infinite loops!
3. Use onErrorCaptured for Safety
Wrap risky child components so errors don’t crash your whole app.
4. Most Common Hook: onMounted
You’ll use this the most for fetching data and setting up things.
🎉 You Did It!
You now understand the complete lifecycle of a Vue component:
- 🥚 Mount - Component is born
- 🐛 Update - Component changes
- 🦋 Unmount - Component says goodbye
- 🛡️ Error - Catching problems
Just like a butterfly, every Vue component goes through its beautiful lifecycle. And now YOU control what happens at each stage! 🦋✨
