Vue Lifecycle Hooks
Every Vue component moves through a predictable sequence of stages from creation to destruction, and lifecycle hooks let your code hook into that sequence at exactly the right moment.
What Is a Lifecycle Hook?
A lifecycle hook is a function you register that Vue calls automatically at a specific point in a component's existence. Instead of guessing when the DOM is ready or when a component is about to disappear, you attach code directly to the moment you care about: right after the component mounts, every time it re-renders, or right before it is torn down. This turns timing-sensitive work -- fetching data, setting up a chart library, starting a timer -- into something declarative rather than something you have to babysit.
The Four Stages of a Component's Life
- Creation -- the component instance is set up and its reactive state is initialized, but nothing has been rendered to the page yet.
- Mounting -- Vue compiles the template, builds the real DOM nodes, and inserts them into the page.
- Updating -- whenever reactive state used in the template changes, Vue re-renders and patches the DOM to match.
- Unmounting -- the component is removed from the page and Vue tears down its reactive effects and watchers.
Registering Hooks with the Composition API
In Vue 3's Composition API, you import hook functions such as onMounted, onUpdated, and onUnmounted from vue and call them inside setup() (or directly inside <script setup>). Each one accepts a callback that Vue queues to run when that stage occurs. You can call the same hook more than once in a single component, and Vue runs every registered callback in the order it was added.
Logging Through a Component's Life
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue'
const count = ref(0)
onMounted(() => {
console.log('Component mounted, count starts at', count.value)
})
onUpdated(() => {
console.log('DOM patched, count is now', count.value)
})
onUnmounted(() => {
console.log('Component removed from the page')
})
</script>
<template>
<button @click='count++'>Clicked {{ count }} times</button>
</template>Options API Equivalents
The Same Component, Options API Style
export default {
data() {
return { count: 0 }
},
created() {
console.log('Reactive state ready:', this.count)
},
mounted() {
console.log('Now attached to the DOM')
},
updated() {
console.log('DOM patched, count is', this.count)
},
unmounted() {
console.log('Component gone')
}
}Cleaning Up in onUnmounted
Any hook that sets something up -- a timer, a native event listener, a WebSocket connection, a third-party library instance -- should have a matching cleanup step in onUnmounted. Skipping this is one of the most common sources of memory leaks in Vue apps, especially in single-page apps where components mount and unmount constantly as users navigate around.
Pairing Setup and Teardown
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const width = ref(window.innerWidth)
function updateWidth() {
width.value = window.innerWidth
}
onMounted(() => {
window.addEventListener('resize', updateWidth)
})
onUnmounted(() => {
window.removeEventListener('resize', updateWidth)
})
</script>
<template>
<p>Window width: {{ width }}px</p>
</template>Exercise: Vue Lifecycle Hooks
When is onMounted guaranteed to run relative to the component's DOM?