The `mounted()` Hook in Vue
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.