Vue Component Lifecycle

Jan 8, 2020

Vue lifecycle hooks give you the opportunity to run code whenever Vue performs a certain action on your component. The hooks that Vue exposes for every component are:

The above list is in order. So Vue always calls beforeCreate before created, and in turn Vue calls created before beforeMount.

To tell Vue to call a function on a given lifecycle hook, you simply add a method to your Vue instance or Vue component with the hook name as the property name:

// The below Vue instance has a `created` hook
const app = new Vue({
  created: function() {
    console.log('Called!');
  },
  template: `
    <h1>Hello, World</h1>
  `
});

// Prints "Called!"
app.$mount('#content');

Created

The created hook runs just after Vue creates an instance of your Vue instance or component. In particular, created runs before mounted and before the first render of the component. Since the component isn't rendered yet, the $el property will be undefined.

You can use an async function as a created hook, but Vue won't wait for the async function to finish before rendering the component.

// This Vue instance has an async created hook
const app = new Vue({
  data: () => ({ answer: null }),
  created: async function() {
    await new Promise(resolve => setTimeout(resolve, 100));
    this.answer = 42;
  },
  // Will first render "The answer is N/A", and then
  // "The answer is 42" after 100 ms
  template: `
    <h1>The answer is {{answer == null ? 'N/A' : answer}}</h1>
  `
});

Similarly, the beforeCreate hook runs just before Vue creates the instance. One major difference between beforeCreate and create is that beforeCreate runs before the Vue instance's data function runs, so any reactive properties will be undefined in beforeCreate:

// This Vue instance has an beforeCreate hook
const app = new Vue({
  data: () => ({ data: 'test' }),
  beforeCreate: function() {
    this.data; // undefined
  },
  template: `<div></div>`
});

Mounted

The mounted hook is the most commonly used hook. Vue runs mounted after Vue "mounts" the component to the DOM. There are two major differences between mounted and created:

  1. When Vue calls mounted, the $el property is defined and set to the DOM element the component is attached to. When Vue calls created, $el is not set.
  2. Vue's official server-side rendering package, vue-server-renderer, runs created hooks but not mounted hooks. This makes sense because, in server-side rendering, the Vue instance is never actually attached to a DOM element, vue-server-renderer simply outputs a string containing HTML.

The mounted hook is often used to send an HTTP request to load data for the component to display. For example, below is an example of using the mounted hook to send an HTTP request to load data about a user:

const url = 'https://jsonplaceholder.typicode.com/users/1';

const app = new Vue({
  data: () => ({ user: null, error: null }),
  // Display username if available, and error message if not
  template: `
    <div>
      <div v-if="user != null">
        {{user.name}}
      </div>
      <div v-if="error != null">
        {{error.message}}
      </div>
    </div>
  `,
  mounted
});

async function mounted() {
  try {
    this.user = await axios.get(url).then(res => res.data);
    this.error = null;
  } catch (error) {
    this.user = null;
    this.error = error;
  }
}

Whether you use created or mounted to fetch data over HTTP is open to debate. The created hook runs first, which means you can parallelize fetching and rendering. But, on the other hand, Vue's server side rendering doesn't wait for async created hooks to finish running, so you need to handle it yourself.

On the other hand, mounted runs after the component is mounted, which means you can ensure a loading screen is shown before sending data to the server. Plus it is easy to manually call a mounted hook for server-side rendering, so long as the mounted hook doesn't rely on the DOM element $el. For example, here's how you call the mounted() function from the previous example on the server side:

await mounted.call(app);
const data = await renderToString(app);
// The HTML output contains the user data
assert.ok(data.includes('Leanne Graham'));

The beforeMount hook differs from the mounted hook in that the $el property still isn't set. But, on the other hand, Vue also doesn't run beforeMount when doing server-side rendering.

Updated

Vue runs the updated hook whenever it needs to re-render part of the component after the component is mounted. Like mounted, Vue doesn't run updated hooks when using server-side rendering.

  window.numUpdated = 0;

  const app = new Vue({
    data: () => ({ count: 0 }),
    updated: function() {
      // Will print every time you click on the h1
      console.log(++window.numUpdated);
    },
    template: '<h1 v-on:click="++count">Clicked {{count}}</h1>'
  });

  app.$mount('#content');

The updated and beforeUpdate hooks are typically useful only for profiling and debugging. For example, you can plug in a print statement to see when Vue needs to update, or track how long it took Vue to update by storing the current time Date.now() in beforeUpdate and calculating the difference in updated. You can't get a description of the necessary updates.

Destroyed

Vue calls the destroyed and beforeDestroy hooks when the Vue instance is unmounted from the DOM. Vue calls beforeDestroy immediately before the instance is unmounted, and destroyed immediately after. For example, if you create a component for every element in an array elements using v-for, Vue will call destroyed every time you remove an element from elements.

  window.numDestroyed = 0;

  Vue.component('test', {
    destroyed: () => ++window.numDestroyed,
    props: ['num'],
    template: '<div class="test-element">{{num}}</div>'
  });

  const app = new Vue({
    data: () => ({ elements: [1, 2, 3, 4] }),
    destroyed: function() {
      // Will print every time you click on the button, because
      // Vue unmounts a `test` component every time you remove
      // an element from `elements`.
      console.log(++window.numDestroyed);
    },
    template: `
      <div>
        <test v-for="el in elements" :num="el"></test>
        <button v-on:click="elements.splice(0, 1)">
          Remove First
        </button>
      </div>
    `
  });

  app.$mount('#content');

The destroyed hook is usually used for cleaning up global state, like calling clearInterval() if you started an interval in mounted.


More Vue Tutorials