Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at how to define and use dynamic and async components.
keep-alive with Dynamic Components
We sometimes want to maintain the state or avoid re-rendering for performance reasons when we switch between components dynamically.
For example, we can use it as follows to keep the states of components when switching between them:
src/index.html
:
Vue.component("post", {
data() {
return {
showDetails: false
};
},
template: `
<div>
<h1>Title</h1>
<button @click='showDetails = !showDetails'>
Toggle Details
</button>
<div v-if='showDetails'>
Lorem ipsum dolor sit amet.
</div>
</div>
`
});
Vue.component("archive", {
data() {
return {
showDetails: false
};
},
template: `
<div>
<h1>Archive</h1>
</div>
`
});
new Vue({
el: "#app",
data: {
tabName: "post"
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button @click'tabName = "post"'>Post</button>
<button @click='tabName = "archive"'>Archive</button>
<keep-alive>
<component v-bind:is="tabName"></component>
</keep-alive>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then when we click Toggle Detail to show the text in the Post tab, we’ll see that it’ll still be shown after switching to the Archive tab and back.
This is because we wrapped keep-alive
around our component
element.
keep-alive
requires that the components being switched between to all have names.
Async Components
We can create components that are loaded from the server only when it’s needed with async components.
To do this, instead of passing in an object as the second argument, we pass in a function that returns a promise instead. For example, we can write the following:
src/index.js
:
Vue.component("async-component", (resolve, reject) => {
setTimeout(() => {
resolve({
template: "<div>Async component</div>"
});
}, 1000);
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<async-component></async-component>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then we should see ‘Async component’ shown on the screen after 1 second.
We see that the function above takes a resolve
function from the parameter and calls it when we want to retrieve the definition from the server.
To indicate loading has failed, we can call reject(reason)
to do that.
Another example would be using it with Webpack code-splitting as follows:
Vue.component('async-webpack', function (resolve) {
require(['./async-component'], resolve)
})
We can also use the import
function to import a component as follows:
Vue.component(
'async-webpack',
() => import('./async-component')
)
If we want to register a component locally, we can write:
new Vue({
// ...
components: {
'component': () => import('./async-component')
}
})
Handling Loading State
We can return a promise that has an object to referencing components for loading and error.
For example, we can use it with an app that’s generated by the Vue CLI by running npx vue create app
where app
is the app name. Then we can select the default choices within the wizard.
Then we write the code as follows:
App.vue
:
<template>
<div id="app">
<AsyncComponent/>
</div>
</template>
<script>
import Loading from "./components/Loading";
import Error from "./components/Error";
import HelloWord from "./components/HelloWorld";
const AsyncComponent = () => ({
component: new Promise(resolve => {
setTimeout(() => {
resolve(HelloWord);
}, 1000);
}),
loading: Loading,
error: Error,
delay: 0,
timeout: 3000
});
export default {
name: "App",
components: {
AsyncComponent
}
};
</script>
<style>
</style>
components/HelloWorld.vue
:
<template>
<div>Hello</div>
</template>
components/Error.vue
:
<template>
<div>Error</div>
</template>
component/Loading.vue
:
<template>
<div>Loading</div>
</template>
In App.vue
, the following code:
component: new Promise(resolve => {
setTimeout(() => {
resolve(HelloWord);
}, 1000);
})
delays loading of the HelloWorld
component for a second. delay
is the number of milliseconds before the loading component is shown.
timeout
is the number of milliseconds until Vue stops trying to load the component.
Therefore, we’ll see Loading from the Loading
component as the HelloWorld
component is loading.
This kind of component definition is available since Vue 2.3.0. Vue Router 2.4.0 should be used if we want to use the above syntax for route components.
Conclusion
We can use keep-alive
to keep the current state of the component as it’s being rendered dynamically with the component
element.
We can define async components with Vue.component
by passing in a function that returns a promise.
In Vue 2.3.0 or later, we can define an async component by creating a function that returns an object that has the promise that resolves to the component as the component that’s rendered.
We can also specify the Loading and Error components in addition to the delay for loading the Loading component and timeout until Vue stops trying to load the component.