loading

Vue Lifecycle Hooks

In Vue, lifecycle hooks are certain points in a component’s lifecycle where we can add code to perform specific tasks.

Lifecycle Hooks

A certain function is called upon each time a component enters a new phase of its lifecycle, and we have the ability to add code to that method. Lifecycle hooks are functions that allow us to “hook” our code into a specific step of the process.

All of a component’s lifecycle hooks are as follows:

  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeUnmount
  8. unmounted
  9. errorCaptured
  10. renderTracked
  11. renderTriggered
  12. activated
  13. deactivated
  14. serverPrefetch

Here are some illustrations of these lifecycle hooks.

The 'beforeCreate' Hook

Before the component is initialized, or before Vue has set up the component’s data, computed properties, methods, and event listeners, the beforeCreate lifecycle hook takes place.

For example, we can use the beforeCreate hook to set up a global event listener. However, since data, watchers, and methods are not yet generated, we should refrain from attempting to access component items via the beforeCreate lifecycle hook.

Furthermore, since DOM elements are not formed until after the component is mounted, attempting to access them from the beforeCreate lifecycle hook is illogical.

Example

CompOne.vue:

				
					<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  beforeCreate() {
		this.text = 'initial text'; // This line has no effect
    console.log("beforeCreate: The component is not created yet.");
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'beforeCreate' Lifecycle Hook</h1>
  <p>We can see the console.log() message from 'beforeCreate' lifecycle hook, but there is no effect from the text change we try to do to the Vue data property, because the Vue data property is not created yet.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
				
			

Line 15 of CompOne.vue in the example above has no effect since it tries to modify the text inside a Vue data property, even though the property hasn’t been created yet. Additionally, don’t forget to view the output of the console.log() call on line 16 by opening the browser console.

The 'created' Hook

Vue has already set up the component’s data, computed properties, methods, and event listeners before the created lifecycle hook occurs.

Since DOM elements cannot be accessed before the component is mounted, we should refrain from attempting to access them from the created lifecycle hook.

You may put up initial data values or retrieve data using HTTP requests by using the created lifecycle hook. The initial value of the data property ‘text’ is set as follows:

Example

CompOne.vue:

				
					<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p id="pResult">{{ text }}</p>
</template>

<script>
export default {
	data() {
		return {
			text: '...'
		}
	},
  created() {
		this.text = 'initial text';
    console.log("created: The component just got created.");
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'created' Lifecycle Hook</h1>
  <p>We can see the console.log() message from 'created' lifecycle hook, and the text change we try to do to the Vue data property works, because the Vue data property is already created at this stage.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pResult {
  background-color: lightcoral;
  display: inline-block;
}
</style>
				
			

The 'beforeMount' Hook

Just before to the component being mounted and so added to the DOM, the beforeMount lifecycle hook takes place.

Since DOM elements cannot be accessed before the component is mounted, we should refrain from attempting to access them from the beforeMount lifecycle hook.

As can be seen in the example below, line 11 in CompOne.vue is broken and causes an error to appear in the browser console. We are still unable to access DOM elements within the component.

Example

CompOne.vue:

				
					<template>
    <h2>Component</h2>
    <p>This is the component</p>
    <p ref="pEl" id="pEl">We try to access this text from the 'beforeMount' hook.</p>
</template>

<script>
export default {
  beforeMount() {
    console.log("beforeMount: This is just before the component is mounted.");
    this.$refs.pEl.innerHTML = "Hello World!"; // <-- We cannot reach the 'pEl' DOM element at this stage 
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'beforeMount' Lifecycle Hook</h1>
  <p>We can see the console.log() message from the 'beforeMount' lifecycle hook, but the text change we try to do to the 'pEl' paragraph DOM element does not work, because the 'pEl' paragraph DOM element does not exist yet at this stage.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
#pEl {
  background-color: lightcoral;
  display: inline-block;
}
</style>
				
			

The 'mounted' Hook

We may add our logic to the mounted() function, which is run immediately after a component is added to the DOM tree.

This is the first opportunity we have to perform operations on DOM elements that are part of the component, such as using the $refs object and the ref attribute, as we do in the second example down below.

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>Right after this component is added to the DOM, the mounted() function is called and we can add code to that mounted() function. In this example, an alert popup box appears after this component is mounted.</p>
  <p><strong>Note:</strong> The reason that the alert is visible before the component is visible is because the alert is called before the browser gets to render the component to the screen.</p>
</template>

<script>
export default {
  mounted() {
    alert("The component is mounted!");
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'mounted' Lifecycle Hook</h1>
  <button @click="this.activeComp = !this.activeComp">Create component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
				
			

Note: In the example above, the alert() is visible PRIOR to the component; however, the mounted stage occurs AFTER the component is placed to the DOM. The reason for this is because the component is first added to the DOM, but the mounted stage occurs and the alert() becomes visible, pausing the browser’s rendering of the component before it can be rendered on the screen.

Here’s an illustration that might be more helpful: to mount the form component and place the cursor inside the input field so that the user can begin typing.

Example

CompOne.vue:

				
					<template>
  <h2>Form Component</h2>
  <p>When this component is added to the DOM tree, the mounted() function is called, and we put the cursor inside the input element.</p>
  <form @submit.prevent>
    <label>
      <p>
        Name: <br>
        <input type="text" ref="inpName">
      </p>
    </label>
    <label>
      <p>
        Age: <br>
        <input type="number">
      </p>
    </label>
    <button>Submit</button>
  </form>
  <p>(This form does not work, it is only here to show the mounted lifecycle hook.)</p>
</template>

<script>
  export default {
    mounted() {
      this.$refs.inpName.focus();
    }
  }
</script>
				
			

The 'beforeUpdate' Hook

Every time there is a modification to our component’s data, but before the update is displayed on the screen, the beforeUpdate lifecycle hook is triggered. The updated lifecycle hook is immediately preceded by the beforeUpdate lifecycle hook.

One unique feature of the beforeUpdate hook is that it allows us to make changes to the application without causing a fresh update, which helps us avoid the otherwise never-ending loop. This is the rationale behind not making any modifications to the application using the updated lifecycle hook, as doing so will result in the creation of an infinite loop. Just glance at the red-colored third example from below.

Example

To show that the beforeUpdate() function has run, the beforeUpdate() function appends a <li> tag to the document.

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>
				
			

App.vue:

				
					<template>
  <h1>The 'beforeUpdate' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is 'updated' and the 'beforeUpdate' hook happens just before that.</p>
  <p>It is safe to modify our page in the 'beforeUpdate' hook like we do here, but if we modify our page in the 'updated' hook, we will generate an infinite loop.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <ol ref="divLog"></ol>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  beforeUpdate() {
    this.$refs.divLog.innerHTML += "<li>beforeUpdate: This happened just before the 'updated' hook.</li>";
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
				
			

The 'updated' Hook

Our component updates its DOM tree before calling the updated lifecycle hook.

Example

Using console.log(), the updated() function generates a message. This occurs each time the page is modified—in this case, each time a component is added or withdrawn.

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>
				
			

App.vue:

				
					<template>
  <h1>The 'updated' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is updated and the updated() function is called. In this example we use console.log() in the updated() function that runs when our application is updated.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  updated() {
    console.log("The component is updated!");
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
				
			

After ten clicks on the “Add/Remove Component” button, the outcome is visible in the browser console:

Vue Lifecycle Hooks -

Note: When the updated lifecycle hook is invoked, we have to be careful not to change the page itself since this will cause the page to update infinitely.

Let’s try what happens if we follow the advice in the following remark exactly. Will there be continuous page updates?

Example

The updated() function adds text to a paragraph, which causes the page to be updated once again. This process repeats endlessly. Fortunately, your browser will eventually break this loop.

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>This is the component</p>
</template>
				
			

App.vue:

				
					<template>
  <h1>The 'updated' Lifecycle Hook</h1>
  <p>Whenever there is a change in our page, the application is updated and the updated() function is called.</p>
  <p>The first change that causes the updated hook to be called is when we remove the component by clicking the button. When this happens, the update() function adds text to the last paragraph, which in turn updates the page again and again.</p>
  <button @click="this.activeComp = !this.activeComp">Add/Remove Component</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
  <div>{{ text }}</div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true,
      text: "Hello, "
    }
  },
  updated() {
    this.text += "hi, ";
  }
}
</script>

<style>
#app {
  max-width: 450px;
}
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  width: 80%;
  background-color: lightgreen;
}
</style>
				
			

This is the console warning that appears in the Chrome browser when you execute the code above locally in dev mode on your computer:

Vue Lifecycle Hooks -

The 'beforeUnmount' Hook

Shortly before a component is deleted from the DOM, the beforeUnmount lifecycle hook is invoked.

The example below shows that the beforeUnmount hook still allows us to access component elements in the DOM.

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p ref="pEl">Strawberries!</p>
</template>
  
<script>
export default {
  beforeUnmount() {
    alert("beforeUnmount: The text inside the p-tag is: " + this.$refs.pEl.innerHTML);
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>Lifecycle Hooks</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
				
			

The 'unmounted' Hook

A component’s removal from the DOM triggers the calling of the unmounted lifecycle hook.

For instance, you can use this hook to eliminate event listeners and stop timers or intervals.

The unmounted() function is invoked when a component is unmounted, and we may add our code to it:

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>When this component is removed from the DOM tree, the unmounted() function is called and we can add code to that function. In this example we create an alert popup box when this component is removed.</p>
</template>

<script>
export default {
  unmounted() {
    alert("The component is removed (unmounted)!");
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>Lifecycle Hooks</h1>
  <button @click="this.activeComp = !this.activeComp">{{ btnText }}</button>
  <div>
    <comp-one v-if="activeComp"></comp-one>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: true
    }
  },
  computed: {
    btnText() {
      if(this.activeComp) {
        return 'Remove component'
      }
      else {
        return 'Add component'
      }
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin: 10px;
    width: 400px;
    background-color: lightgreen;
  }
</style>
				
			

Note: In the example above, the alert() is shown PRIOR to the component disappearing, but the unmounted stage occurs AFTER the component is gone from the DOM. This is because, although the component is first deleted from the DOM, the unmounted stage occurs first, making the alert() visible and preventing the browser from visually removing the component before it is rendered on the screen.

The 'errorCaptured' Hook

The mistakeIf a child or descendant component has an errorCaptured lifecycle hook is triggered.

This hook can be used to log errors, handle errors, or show errors to the user.

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>This is the component</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'errorCaptured' Lifecycle Hook</h1>
  <p>Whenever there is an error in a child component, the errorCaptured() function is called on the parent.</p>
  <p>When the button inside the component is clicked, a method will run that tries to do changes to a $refs object that does not exist. This creates an error in the component that triggers the 'errorCaptured' lifecycle hook in the parent, and an alert box is displayed with information about the error.</p>
  <p>After clicking "Ok" in the alert box you can see the error in the browser console.</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured() {
    alert("An error occurred");
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
				
			

Information regarding the mistake can also be collected as parameters to the errorCaptured() function and these arguments are:

  1. the error
  2. the component that triggered the error
  3. the error source type

In the example below these arguments are captured in the errorCaptured() function and sent to the console:

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>This is the component</p>
  <button @click="generateError">Generate Error</button>
</template>

<script>
export default {
  methods: {
    generateError() {
      this.$refs.objEl.innerHTML = "hi";
    }
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'errorCaptured' Lifecycle Hook</h1>
  <p>Whenever there is an error in a child component, the errorCaptured() function is called on the parent.</p>
  <p>Open the browser console to see the captured error details.</p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<script>
export default {
  errorCaptured(error,compInst,errorInfo) {
    console.log("error: ", error);
    console.log("compInst: ", compInst);
    console.log("errorInfo: ", errorInfo);
  }
}
</script>

<style>
#app > div {
  border: dashed black 1px;
  border-radius: 10px;
  padding: 10px;
  margin-top: 10px;
  background-color: lightgreen;
}
</style>
				
			

The 'renderTracked' and 'renderTriggered' Lifecycle Hooks

When a render function is configured to track, or monitor, a reactive component, the renderTracked hook is triggered. Normally, the renderTracked hook is triggered upon initialization of a reactive component.

When a tracked reactive component changes, the renderTriggered hook is triggered, which causes a fresh render to update the screen with the most recent modifications.

A variable component is known as a reactive component.

A render function is a Vue-compiled function that manages reactive elements. The render function is called when a reactive component changes, and it rerenders the application on the screen.

Only in development mode, the renderTracked and renderTriggered hooks are intended for debugging purposes.

You must copy the code in the example below to your computer and run the program in development mode in order to see the alert() and console.log() from the renderTracked and renderTriggered hooks.

Example

CompOne.vue:

				
					<template>
  <h2>Component One</h2>
  <p>This is a component.</p>
  <button @click="counter++">Add One</button>
  <p>{{ counter }}</p>
</template>
  
<script>
export default {
  data() {
    return {
      counter: 0
    }
  },
  renderTracked(evt) {
    console.log("renderTracked: ",evt);
    alert("renderTracked");
  },
  renderTriggered(evt) {
    console.log("renderTriggered: ",evt)
    alert("renderTriggered");
  }
}
</script>
				
			

App.vue:

				
					<template>
  <h1>The 'renderTracked' and 'renderTriggered' Lifecycle Hooks</h1>
  <p>The 'renderTracked' and 'renderTriggered' lifecycle hooks are used for debugging.</p>
  <p><mark>This example only works in development mode, so to see the hooks run, you must copy this code and run it on you own computer in development mode.</mark></p>
  <div>
    <comp-one></comp-one>
  </div>
</template>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
				
			

Note: Because the renderTracked and renderTriggered hooks only function in development mode, the code in the aforementioned example is meant to be copied and run locally on your computer.

The 'activated' and 'deactivated' Lifecycle Hooks

The mounted and unmounted lifecycle hooks for when a component is added or removed from the DOM are visible as we can see above on this page.

The lifecycle hooks marked as activated and deactivated are used to add and remove cached dynamic components—not DOM elements. The dynamic component in the example below is cached using the <KeepAlive> tag.

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>Below is a log with every time the 'mounted' or 'activated' hooks run.</p>
  <ol ref="olEl"></ol>
  <p>You can also see when these hooks run in the console.</p>
</template>
  
<script>
export default {
  mounted() {
    console.log("mounted");
    const liEl = document.createElement("li");
    liEl.innerHTML = "mounted";
    this.$refs.olEl.appendChild(liEl);
  },
  activated() {
    console.log("activated");
    const liEl = document.createElement("li");
    liEl.innerHTML = "activated";
    this.$refs.olEl.appendChild(liEl);
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>
				
			

App.vue:

				
					<template>
  <h1>The 'activated' Lifecycle Hook</h1>
  <p>In this example for the 'activated' hook we check if the component is cached properly with <KeepAlive>.</p>
  <p>If the component is cached properly with <KeepAlive> we expect the 'mounted' hook to run once the first time the component is included (must be added to the DOM the first time), and we expect the 'activated' hook to run every time the component is included (also the first time).</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
				
			

To see how the activated and deactivated hooks function, let’s enlarge the previous example. In order to see that the mounted hook runs only when the cached component is added initially and that the unmounted hook never runs for the cached component, let’s also use the mounted and unmounted hooks.

Example

CompOne.vue:

				
					<template>
  <h2>Component</h2>
  <p>Below is a log with every time the 'activated', 'deactivated', 'mounted' or 'unmounted' hooks run.</p>
  <ol ref="olEl"></ol>
  <p>You can also see when these hooks run in the console.</p>
</template>
  
<script>
export default {
  mounted() {
    this.logHook("mounted");
  },
  unmounted() {
    this.logHook("unmounted");
  },
  activated() {
    this.logHook("activated");
  },
  deactivated() {
    this.logHook("deactivated");
  },
  methods: {
    logHook(hookName) {
      console.log(hookName);
      const liEl = document.createElement("li");
      liEl.innerHTML = hookName;
      this.$refs.olEl.appendChild(liEl);
    }
  }
}
</script>

<style>
  li {
    background-color: lightcoral;
    width: 5em;
  }
</style>
				
			

App.vue:

				
					<template>
  <h1>The 'activated' and 'deactivated' Lifecycle Hooks</h1>
  <p>In this example for the 'activated' and 'deactivated' hooks we also see when and if the 'mounted' and 'unmounted' hooks are run.</p>
  <button @click="this.activeComp = !this.activeComp">Include component</button>
  <div>
    <KeepAlive>
      <comp-one v-if="activeComp"></comp-one>
    </KeepAlive>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: false
    }
  }
}
</script>

<style scoped>
  div {
    border: dashed black 1px;
    border-radius: 10px;
    padding: 20px;
    margin-top: 10px;
    background-color: lightgreen;
  }
</style>
				
			

The 'serverPrefetch' Lifecycle Hook

Only when server-side rendering (SSR) is occurring does the’serverPrefetch’ hook get called.

It would take a lengthy setup and introduction to explain and create an example for the’serverPrefetch’ hook, which is outside the purview of this article.

Share this Doc

Vue Lifecycle Hooks

Or copy link

Explore Topic