§ Interactive demo
This is a mini Vue.js app with its own router and mount point inside a Markdown component.
The Playground view is loaded asynchronously after you click on
the Playground button.
If you want to see that in action, just open your network tab on your
DevTools, then click on the playground button below.
The basics for the interactive demo are explained below.
I think this post will be most useful to advanced Angular/AngularJS developers that are still pondering what’s all the fuss about Vue.js —just like I was just a few weeks ago.
Nonetheless, I have included step-by-step instructions that will help beginners too.
So, in this —opinionated— tutorial, I’m hoping you’ll:
- Realize just how ludicrously easy is to setup on-demand / lazy-load for Vue.js components.
- Show you how to combine dynamic components + async loading for maximum effect!
§ Use cases
These are the use cases we will review in this tutorial:
- The app router should only load code for sections of the app you actually visit.
- You should be able to dynamically add/remove/swap components on a page.
- Inside a section, the app should only load code for components that are actually rendered.
A component might be declared an available for rendering, but its code should only load if you display it.
§ Prerequisites
You can setup a new Vue.js app following this guide.
§ Asynchronous route loading
To showcase this feature let’s create a new SFC (Single File Component).
Don’t you just love .vue files?
They are like little packages composed of a template, a script
and a style section in the same file.
This allows for very powerful modularization and code reuse.
Create a src/views/Playground.vue
file with this content:
<template lang="pug">
v-container
h1 Welcome to the component playground
</template>
Taking advantage of Webpack’s code chunking we can make our app’s initial rendering very fast by only loading the code we need at the beginning and loading other parts on-demand.
Let’s make our new Playground.vue
component to load asynchronously.
Open the src/router.js
file and modify it to look like this:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
},
{
path: '/playground',
name: 'playground',
component: () => import('@/views/Playground')
}
]
})
To see the async loading in action, open the browser console (press F12) go to the Network tab and visit http://localhost:8080/
Now visit http://localhost:8080/#/playground and observe how it makes
a request for a 0.js
file when you change the URL.
The good thing? The app will only make that request once, then it’ll be cached afterwards!
This is the line of code that makes this possible:
component: () => import('@/views/Playground')
How hard is this for async loading of router level components?
Pretty damn easy I bet! :)
§ Dynamic component rendering
Another thing that is very easy to do in Vue.js is rendering
components dynamically.
Just have a look for yourself and judge.
§ Defining new components
Let’s create three components to use on the Playground:
src/components/SimpleButton.vue:
<template lang="pug"> v-btn.mr-4.mb-4 Simple button </template>
src/components/SimpleCard.vue:
<template lang="pug"> v-card.elevation-4.my-4 v-card-text strong Async loaded card </template>
src/components/SimpleSwitch.vue:
<template lang="pug"> v-switch(hide-details label="Simple switch").my-4 </template>
§ Static rendering
If you want to see your new components in action, modify the src/views/Playground.vue
file to look like this:
<template lang="pug">
v-container
h1 Welcome to the component playground
simple-button
simple-card
simple-switch
</template>
<script>
import SimpleButton from '@/components/SimpleButton'
import SimpleCard from '@/components/SimpleCard'
import SimpleSwitch from '@/components/SimpleSwitch'
export default {
components: { SimpleButton, SimpleCard, SimpleSwitch }
}
</script>
Then visit: http://localhost:8080/#/playground
§ Dynamic rendering
What we’ll do in this section is present a <select>
input as means to pick a component from a list and then display it.
This can be accomplished through the powerful <component>
element.
Modify the Playground.vue
file to look like this:
<template lang="pug">
v-container
h1 Welcome to the component playground
select(v-model="selectedComponent")
option(
v-for="(item, index) in componentList"
:key="index"
:value="item.component"
) {{ item.label }}
hr
.dynamic-component
component(:is="selectedComponent")
</template>
<script>
import SimpleButton from '@/components/SimpleButton'
import SimpleCard from '@/components/SimpleCard'
import SimpleSwitch from '@/components/SimpleSwitch'
export default {
data () {
return {
componentList: [
{
component: SimpleButton,
label: 'A button'
},
{
component: SimpleCard,
label: 'A card'
},
{
component: SimpleSwitch,
label: 'A switch'
}
],
selectedComponent: null
}
}
}
</script>
<style lang="sass" scoped>
.dynamic-component
border: 1px dashed red
display: inline-block
padding: 1em
</style>
Visit: http://localhost:8080/#/playground and use the select input.
Awesome, right?
§ Async loading + dynamic rendering
What do you think would take to enable async loading for the
SimpleCard
component above?
Well, you’ll only need to change Playground.vue
to this:
<template lang="pug">
v-container
h1 Welcome to the component playground
select(v-model="selectedComponent")
option(
v-for="(item, index) in componentList"
:key="index"
:value="item.component"
) {{ item.label }}
hr
.dynamic-component
component(:is="selectedComponent")
</template>
<script>
import SimpleButton from '@/components/SimpleButton'
// Line below is commented since we will be loading it asynchronously
// import SimpleCard from '@/components/SimpleCard'
import SimpleSwitch from '@/components/SimpleSwitch'
export default {
data () {
return {
componentList: [
{
component: SimpleButton,
label: 'A button'
},
{
// Async loading!
component: () => import('@/components/SimpleCard'),
label: 'A card'
},
{
component: SimpleSwitch,
label: 'A switch'
}
],
selectedComponent: null
}
}
}
</script>
<style lang="sass" scoped>
.dynamic-component
border: 1px dashed red
display: inline-block
padding: 1em
</style>
Very easy, don’t you think?
You can verify it’s loading asynchronously by watching the Network
tab in your browser console.
Then select the A card option.
At that moment a request will be made to retrieve that component’s code!
§ Staying alive
The astute one might have noticed that whatever state you set on the
SimpleSwitch
component is lost when you switch to another component.
If you want to preserve on memory what’s on these dynamic components,
all you have to do is surround the <component>
element with
<keep-alive>
tags like this:
<template lang="pug">
v-container
h1 Welcome to the component playground
select(v-model="selectedComponent")
option(
v-for="(item, index) in componentList"
:key="index"
:value="item.component"
) {{ item.label }}
hr
.dynamic-component
keep-alive
component(:is="selectedComponent")
</template>
Go try it out! Pick A switch, switch it on, then pick something else, then return to A switch and you’ll see it preserved its state. Awesome!
§ Some snarky comments
Do you know what other thing is a testament to Vue’s power and flexibility?
That Vuetify is a more advanced, featureful and complete implementation of the Material Design Guidelines than Angular Material itself.
Go figure!
If you are an Angular developer, compare the stuff we did above with how you’d do lazy loading in Angular.