If you have recently started learning about a framework or Front-End library, you may have already come across the term "lifecycle" of a component and may have wondered what exactly it means.
In this article, we will understand the main lifecycle methods that Vue.js offers us and their implementations with Options API and Composition API!
Table of Contents
What is a lifecycle
Lifecycle hooks with Options API
Creation Hooks
Mounting Hooks
Update Hooks
Unmounting Hooks
Lifecycle hooks with Composition API
Setup Hook
Syntax with Composition API
Wrapping it up
What is a lifecycle?
In Vue, each component goes through several steps: basically, it is created, it observes data, it mounts its elements in the DOM and updates them when any data is modified until, eventually, it is destroyed. Throughout this process, the component uses several functions called lifecycle methods or lifecycle hooks.
The lifecycle hooks are predefined methods that are executed at specific times during the useful life of a component, i.e. the period of time in which a component exists, is active and performs its functions in an application or software system.
These methods can also be used by us, developers, and allow us to control the component's behavior, perform operations at specific times and interact with the DOM or other components.
Lifecycle hooks with Options API
With the Options API we have access to eight main lifecycle methods (which we will call hooks from here on): beforeCreate
, created
, beforeMount
, mounted
, beforeUpdate
, updated
, beforeUnmount
and unmounted
.
The moment at which each hook is executed in the component instance is demonstrated by the diagram below, taken from the official Vue.js documentation:
To facilitate the study of this article, we'll categorize them into groups: creation, mounting, update, and unmounting hooks.
Creation Hooks
The creation hooks are the first hooks to run in a component. They allow you to perform actions before your component is added to the DOM and are also the only ones that work with server-side rendering.
Here we will learn about the hooks beforeCreate and created.
beforeCreate
beforeCreate
is executed as soon as a component is initialized. At this time, computed states and properties have not yet been processed.
<script>
export default {
beforeCreate() {
alert('beforeCreate was called')
}
}
</script>
In the code above, alert()
is executed before the component is rendered.
This hook is very useful, for example, to make an authentication request. Depending on the result of the request, the global state (Vuex or Pinia) is configured accordingly, allowing you to handle authentication before any content is displayed on the screen.
created
created
is also executed before the component is mounted in the DOM, however, at this stage all states, computed properties, methods and watchers have already been processed and are ready to be accessed.
<script>
export default {
data() {
return { message: 'BEM VINDO À ALURA' }
},
created() {
alert(this.message)
}
}
</script>
In the code above, alert()
is executed before the component is rendered, but we already have access to the message
property processed in our data()
.
This is a very useful hook to make a HTTP request to an API and update the component state with these data, which will be rendered in the DOM.
Mounting Hooks
Mounting hooks allow you to access the component immediately before or after inserting the component into the DOM (first render), typically to access or modify elements.
Here we will learn about the hooks beforeMount and mounted.
beforeMount
beforeMount
is very similar to created
, with the key distinction lying in the timing of their execution::
-
created
is triggered after component creation and allows access to data options, but before the DOM is mounted; -
beforeMount
is triggered immediately before mounting the DOM, right after a pre-compilation of the component'stemplate
andstyle
.
<script>
export default {
data() {
return { pageTitle: 'Loading...' }
},
beforeMount() {
// Simulating the return of an API
setTimeout(() => {
const apiResponse = { title: 'Page Title' }
this.pageTitle = apiResponse.title
}, 2000)
},
}
</script>
<template>
<h1>{{ pageTitle }}</h1>
</template>
In the code above, we simulate an API call that takes two seconds to update the page title. The same effect would be achieved using created
.
mounted
mounted
is executed immediately after the component is mounted. At this stage, the component becomes functional: properties from data()
are injected into the template, elements are rendered, and DOM manipulation becomes possible.
<script>
export default {
data() {
return {
items: [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' }
]
}
},
mounted() {
const listItems = this.$refs.listItem
listItems.forEach((item) => {
item.addEventListener('click', () => {
alert(`You've clicked: ${item.textContent}`)
})
})
}
}
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id" ref="listItem">
{{ item.name }}
</li>
</ul>
</template>
In the example above, we created a dynamic list in the template, but the click events for each <li>
in the list were only inserted after the component's mounted()
hook is executed.
Update Hooks
Update hooks are executed whenever a reactive property used in your component changes, forcing it to re-render. This execution can be immediately before or after this change.
Here we will learn about the hooks beforeUpdate and updated.
beforeUpdate
beforeUpdate
runs immediately after a reactive property is changed, but immediately before the component is re-rendered on screen.
<script>
export default {
data() {
return {
counter: 0,
history: []
}
},
beforeUpdate() {
this.history.push(this.counter)
},
methods: {
increment() {
this.counter++
}
}
}
</script>
In the code above, for example, whenever counter
is updated, we will save its new value in the array history
before this data is updated on screen. beforeUpdate
is also widely used for debugging operations to identify when component rendering is activated, for example.
updated
updated
is executed immediately after a re-render of the component caused by a reactive data change, being very useful for creating side effects related to the DOM.
<script>
export default {
data() {
return {
items: []
}
},
methods: {
addItem(item) {
this.items.push(item)
}
},
updated() {
const listContainer = this.$refs.listContainer
listContainer.scrollTop = listContainer.scrollHeight
}
}
</script>
In the code above, whenever a new item is added to the list using the addItem
method, the component is updated, and the updated
hook is called. Inside the updated
hook, we implement an auto-scroll functionality so that the list scrolls to the last item whenever new items are added to the list.
Unmounting Hooks
Unmounting hooks, also called destruction hooks (a bit dramatic, right?) are executed immediately before or after a component is unmounted.
Here we will learn about the hooks beforeUnmount and unmounted.
beforeUnmount
beforeUnmount
is executed immediately before the component is unmounted. Importantly, at this point the component is still fully functional, so it's a great step to perform clean up functions such as removing event listeners, deleting variables, performing logout actions, etc.
<script>
export default {
data() {
return {
socket: null
};
},
mounted() {
this.socket = new WebSocket('wss://example.com/socket');
this.socket.addEventListener('message', this.handleMessage);
},
beforeUnmount() {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.removeEventListener('message', this.handleMessage);
this.socket.close();
}
},
methods: {
handleMessage(event) {
// Logic for dealing with the received WebSocket messages
}
}
};
</script>
In this example, the component creates a WebSocket connection when it is mounted and adds a listener for WebSocket messages. In beforeUnmount
, it checks if this connection is open (WebSocket.OPEN
) and, if so, closes the connection using this.socket.close()
. This ensures that the connection is properly closed before the component is destroyed, preventing resource leaks and unexpected behavior.
unmounted
unmounted
happens immediately after the component is unmounted from the DOM tree, so practically everything that existed in relation to that component is considered "destroyed", as emphasized by the documentation itself:
A component is considered unmounted after:
- All its child components have been unmounted;
- All its associated reactive effects (rendering effects and data created during
setup()
) have been stopped.
We can conclude that, at this stage, there is not much that can be done to a component. In general, unmounted
is also used for clean up functions.
Lifecycle hooks with Composition API
The Composition API, which represents the latest syntax for a Vue component, has introduced alterations to the lifecycle methods. The most noticeable changes include the removal of the hooks beforeCreate and created, along with subtle modifications to their names and syntax.
These changes came to boost the performance of our Vue application, in addition to providing better compatibility with TypeScript. All of this was possible thanks to the new hook setup()
.
Let's understand a bit more about this.
Setup Hook
The setup()
hook in Vue 3 is an essential part of the Composition API, which is a more flexible, functional, and modular approach to creating components compared to the Vue 2 syntax. It is used to configure and initialize the state, props, methods and other options of a Vue component.
However, despite being a feature of the Composition API, it is entirely possible to use setup()
within the Options API as a facilitator for your code. See what the hook syntax looks like in both cases:
<!-- OPTIONS API -->
<script>
export default {
setup () {
console.log("Setup with Options API");
}
}
</script>
<!-- COMPOSITION API -->
<script setup>
console.log("Setup with Composition API");
</script>
Extinction of beforeCreate and created
If we go back to the lifecycle diagram we saw at the beginning of this article, we can see that setup()
is the first creation method executed in a Vue component. It occurs even before beforeCreate and created are executed!
This way, any code that could be used within these hooks can simply be inserted into setup()
. Do you remember the API request simulation we exemplified earlier? See how it would look using <script setup>
:
<script setup>
import {ref} from 'vue';
let pageTitle = ref('Loading...');
setTimeout(() => {
const apiResponse = {title: 'Page Title'};
pageTitle.value = apiResponse.title;
}, 2000);
</script>
With the code above, we will have the same effect as with created
!
Syntax with Composition API
Although we no longer have beforeCreate and created, the other hooks are still present in the Composition API, but with a slightly different syntax from what we have seen earlier, as we will now be calling each of them within setup ()
.
It is important to keep in mind that the moment of execution for each of these hooks within the component remains the same! Therefore, we will focus here only on their syntax, starting with the change in names, which are now: onBeforeMount
, onMounted
, onBeforeUpdate
, onUpdated
, onBeforeUnmount
and onUnmounted
.
To grasp the new syntax more effectively, it's essential to recognize that within the Composition API, every hook works as a function that takes a callback function as its parameter. For example, here is the onMounted
typing:
function onMounted(callback: () => void): void
Based on this, we can use our hooks in the following ways:
<script setup>
import { ref, onMounted } from 'vue';
const myMessage = ref('A random message')
const showMessage = () => {
alert(myMessage.value);
};
// Method 1:
onMounted(showMessage);
// Method 2:
onMounted(() => showMessage());
// Method 3:
onMounted(() => {
showMessage()
}
</script>
Let’s understand the subtle differences between each of them:
- Method 1:
onMounted(showMessage)
This way, you are directly passing the showMessage
function as an argument to onMounted
. The showMessage
function will be called directly when onMounted
is executed. This is useful when the showMessage
function does not need additional arguments.
- Method 2:
onMounted(() => showMessage())
Here you wrap the showMessage
call in an anonymous callback function. The callback function will be executed when onMounted
is triggered and then it will call showMessage()
. This method is useful when you need to pass arguments to showMessage
.
- Method 3:
onMounted(() => { showMessage() })
This way is similar to Method 2, but you are using an anonymous callback function with a block of code enclosed in curly braces { }
. This is useful when you need to perform multiple actions or logic within onMounted
in addition to calling showMessage
.
The main difference between methods 2 and 3 is that Method 3 allows you to include multiple lines of code and perform multiple actions within onMounted
. Method 2 is more concise and direct, while Method 3 is more expandable when you need more complexity.
The above syntax can be used with any Composition API hook, not just onMounted
. Furthermore, you can use the same lifecycle hook multiple times within a single component! Also note that for the hooks you intend to utilize in your component, it's crucial to import them within the <script setup>
:
import { onMounted, onUpdated } from 'vue';
This is another particularity of the Composition API: by importing from the API only what is necessary for each component, we improve the performance of our application, unlike what happens in the Options API, where the entire API is already completely imported into the components behind the scenes.
Wrapping it up
In this article, we delved into the different lifecycle methods provided by Vue.JS and explored how they can enhance our applications.
I trust you found the information enjoyable and that it added to your understanding of Vue. Until next time!
Top comments (2)
A simple but well-written article about one of my biggest fascinations on Vue. You did improve my Friday afternoon!
Congrats on the article.
Thank you so much for the kind words, I'm really glad my article's been of use for you, Gabriel! <3