The `mounted()` Hook in Vue

May 11, 2020

The mounted() hook is the most commonly used lifecycle hook in Vue. Vue calls the mounted() hook when your component is added to the DOM. It is most often used to send an HTTP request to fetch data that the component will then render.

For example, the below Vue component uses the mounted() hook to make an HTTP request to the JSONPlaceholder API.

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;
  }
}

With Async/Await

Notice that the above example uses an async function for the mounted hook. Vue does not block rendering until the mounted function is done running, so mounted() above runs concurrently with axios.get().

Unlike many other frameworks, Vue provides a mechanism for handling errors in async functions. Vue calls a global error handler whenever a lifecycle hook throws an error, whether the error was sync or async.

Vue.config.errorHandler = function (err) {
  console.log(err.message); // "Oops"
};

new Vue({
  template: `<h1>Hello</h1>`,
  mounted: async function() {
    await new Promise(resolve => setTimeout(resolve, 1000));
    throw new Error('Oops');
  }
}).$mount('#content');

Versus created()

Vue has another lifecycle hook that is similar to mounted(): the created() hook. Vue runs the created() hook when the component object is created, before the component is mounted to the DOM.

The Vue docs recommend using the mounted() hook over the created() hook for data fetching. This point is often debated. But there is one key reason why Mastering JS recommends using mounted() for data fetching: because of server-side rendering.

Vue calls the created() hook during server-side rendering, but not the mounted() hook. So that's a point in favor of created(), right?

The problem comes from the fact that data fetching is almost always asynchronous, and Vue's server-side rendering does not wait for async created() hooks to finish.

// 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>
  `
});

let data = await renderToString(app);
data; // Renders "answer is N/A"

On the other hand, it is easy to manually run the mounted() hook when using server-side rendering.

await app.$options.mounted[0].call(app);
let data = await renderToString(app);
data; // Renders "answer is 42"

Or, if you have a reference to the mounted() hook you registered, you can just call it on the app:

await mounted.call(app);
let data = await renderToString(app);
data; // Renders "answer is 42"

Or, you can write separate logic for fetching using server-side rendering, like by calling the database directly rather than going through HTTP. Using mounted() for data fetching gives you more flexibility when using server-side rendering without sacrificing any convenience.


More Vue Tutorials