Using Vue Instance Lifecycle Hooks

Published on April 27, 2017 by

We just took a closer look at a Vue instance’s lifecycle, including which hooks we have at our disposal. In this post, I want to show you how to actually use these lifecycle hooks for running custom code at certain stages of a Vue instance’s lifecycle.

Below is a simple Vue instance with all of the lifecycle hooks.

<div id="app">
  <h1>{{ message }}</h1>
</div>
var vm = new Vue({
	el: '#app',
	data: {
		message: 'Hello World!'
	},
	beforeCreate: function() {
		alert("beforeCreate");
	},
	created: function() {
		alert("created");
	},
	beforeMount: function() {
		alert("beforeMount");
	},
	mounted: function() {
		alert("mounted");
	},
	beforeUpdate: function() {
		alert("beforeUpdate");
	},
	updated: function() {
		alert("updated");
	},
	beforeDestroy: function() {
		alert("beforeDestroy");
	},
	destroyed: function() {
		alert("destroyed");
	}
});

So the only thing that happens, is that a message is being output on the page, and then I have added the lifecycle hooks. Notice that these hooks are named functions and that they are added at the top level of the Vue instance. The keys should match the name of the lifecycle hook, and the function is just an arbitrary function. Note, however, that all of the hook functions have their this context bound to the Vue instance so you can access data properties, methods, etc. As we have seen before, this also means that you cannot use ES6 arrow functions.

So notice that I have added an alert for each lifecycle hook. If you run the above code, the following happens.

First we see that the beforeCreate hook is triggered. At this point, Vue hasn’t really done much yet.

The next hook is the created hook, which is invoked when Vue has set up events and data observation, so now Vue is watching our data properties for changes.

Next up is the beforeMount hook, which is invoked after Vue has compiled the template and updated the virtual DOM. The next step is then to mount the template to the DOM, essentially meaning to add the $el property to the Vue instance and update the native DOM. After doing this, the mounted hook is invoked.

This is the last lifecycle hook that is invoked, because we are not updating any data during the Vue instance’s lifecycle, nor are we destroying it.

Let’s see what happens when we update a data property, so let’s add a button to do this.

<button @click="message = 'A new message'">Change Message</button>

After the hooks that you just saw, we will now see two additional alerts when changing the message by clicking the button, namely for the beforeUpdate and updated hooks. The beforeUpdate hook is invoked before Vue updates the virtual DOM and patches the real DOM, and the updated hook is triggered when the DOM has been updated. Therefore it is entirely possible for you to access the native DOM within this hook as it has already been updated, but you should probably not do this unless you really need to. These two hooks are invoked every time some data within our Vue instance changes. And notice that I said changes. If you click the button again, you will see that no alerts are displayed. This is because now we are no longer changing the value of the message data property. We are indeed assigning a value to it, but the value is the same as the existing one. Vue is therefore smart enough to not trigger the hooks, because there is no need to update the DOM. So this is just a performance optimization that Vue does for us. We have seen it before, but I just wanted to point it out and show that the hooks are not even invoked in this scenario.

Now I know I said that these hooks are invoked every time data changes. While this will usually be the case, it won’t necessarily be true. Let’s try to add a counter data property as well as a button which increases its value.

var vm = new Vue({
	el: '#app',
	data: {
		message: 'Hello World!',
		counter: 1
	}
});
<button @click="counter++">Increase Counter</button>

If you click the button, notice how neither the beforeUpdated nor updated hooks are invoked, even though we have just changed the value of a data property. So why is that? The reason is that these hooks are only invoked when the virtual DOM and thereby the real DOM needs to be updated. Again, Vue is quite smart and understands that we are not actually displaying the counter data property, so there is no need to update the DOM. The value is of course still updated correctly.

On the other hand, if we display the counter, we should see the hooks being invoked.

<button @click="counter++">Increase Counter ({{ counter }})</button>

And indeed, now the hooks are invoked because the DOM needs to be updated in response to changing the value of the counter data property.

Let’s also destroy the Vue instance just so that we will see all of the lifecycle hooks in action.

vm.$destroy();

Now we see that the beforeDestroy hook is invoked, followed by the destroyed hook that is triggered when the Vue instance has been destroyed.

Let’s just uncomment this line, because I want to show you something else instead.

//vm.$destroy();

You saw how the beforeMount and mounted hooks are invoked immediately because we have an el property on our Vue instance. Let’s try to remove this property and see what happens.

var vm = new Vue({
	data: {
		message: 'Hello World!',
		counter: 1
	},
	beforeCreate: function() {
		alert("beforeCreate");
	},
	created: function() {
		alert("created");
	},
	beforeMount: function() {
		alert("beforeMount");
	},
	mounted: function() {
		alert("mounted");
	},
	beforeUpdate: function() {
		alert("beforeUpdate");
	},
	updated: function() {
		alert("updated");
	},
	beforeDestroy: function() {
		alert("beforeDestroy");
	},
	destroyed: function() {
		alert("destroyed");
	}
});

Now only the beforeCreated and created hooks are fired, because while the Vue instance has indeed been created, it has not been mounted to a template, and therefore the string interpolation does not work, and neither do the buttons. Let’s try to mount the Vue instance manually by using the $mount method. I’ll just add a two second timeout so it is easier to see what happens.

setTimeout(function() {
	vm.$mount('#app');
}, 2000);

Now we see the same alerts as before, but after two seconds, we see that the beforeMount hook is invoked.

Notice that at this point, the DOM has not yet been updated, and we still see the string interpolations syntax being displayed on the page. After this hook, the mounted hook immediately follows, at which point the DOM has been updated.

Although it doesn’t appear that way here in JSFiddle due to the alerts, it is indeed the case, and checking the contents of the $el property within the mounted hook will reveal that the DOM has indeed been updated at this point in time.

In this example, we have declared each lifecycle hook as a function. While this is by far the most common approach, you can actually use an array of functions as the value. Let’s try to change the beforeCreated hook to an array of two functions.

beforeCreate: [
  function() {
      alert("beforeCreate #1");
  },
  function() {
      alert("beforeCreate #2");
  }
]

Running the code again, you will see two alerts being displayed for the beforeCreate hook. This is useful if you need to do more than one thing for a given hook, as this allows you to keep each function focused on doing one thing instead of having a single function do many different things. Before moving on, let’s just take a brief moment to see what the Vue instance looks like, so let’s output it to the console.

console.log(vm);

If we take a look at the $options object, then we can see that each hook is in fact an array. If we declare a hook as a function, Vue just takes this function and adds it to an array. The alternative is to do as we just did and add an array of functions. So whenever a given hook is invoked, Vue loops over the hook’s array of functions and invokes each of them.

Okay, so one last thing. As you have seen, lifecycle hooks are added at the top level of a Vue instance. But as you hopefully recall, this is also where Vue adds proxy methods for our data properties, methods, etc. What would happen if we added a created data property, which is also the name of a lifecycle hook? Well, let’s try and see whether or not our code blows up.

var vm = new Vue({
	el: '#app',
	data: {
		message: 'Hello World!',
		counter: 1,
		created: new Date()
	},
	// ...
});

Logically speaking, this should break, because we have a lifecycle hook defined at the top level of the Vue instance. So when Vue processes our data properties and sets up a proxy method for the created property, there will be a collision – right? No, this is actually – and luckily – not the case.

Let’s take a closer look at our Vue instance once again.

Notice that Vue has still added proxy methods for our created data property, and we still see our lifecycle hook being invoked correctly.

If we take a look at the $options property, we can see that there is still a created hook registered.

As you can see, Vue does not add the lifecycle hook at the top level of the Vue instance, even though we added the properties there when we initialized the Vue instance. Instead, Vue keeps track of these hooks internally elsewhere. That’s why there are no collissions between the hooks and any data properties, methods or the like that we add.

Alright, that was a lot of information about lifecycle hooks. Now you should have a pretty clear picture about what happens when you initialize a Vue instance and which hooks are available for you to run some custom code if need be.

Featured

Learn Vue.js today!

Take an online course and become an Vue.js champion!

Here is what you will learn:

  • How to build advanced Vue.js applications (including SPA)
  • How Vue.js works under the hood
  • Communicating with services through HTTP
  • Managing state of large applications with Vuex
  • ... and much more!
Vue.js logo
Author avatar
Bo Andersen

About the Author

I am a back-end web developer with a passion for open source technologies. I have been a PHP developer for many years, and also have experience with Java and Spring Framework. I currently work full time as a lead developer. Apart from that, I also spend time on making online courses, so be sure to check those out!

0 comments on »Using Vue Instance Lifecycle Hooks«

  1. Denni Adam

    Thanks for article, Bo!
    How can I mock lifecycle hooks in unit tests?

  2. Vuejs Lifecycle

    Nice post you had shared here.
    Thanks a lot.

Leave a Reply

Your e-mail address will not be published.