Vue.js and how to include and render components dynamically
Posted on January 24, 2021 in
3 min read
In this post I'm going to show how to create a Vue.js App with an on-the-fly component import capability and how to render them using Vue Router as well.
It's bare-bones, just the mentioned functionality.
Import components dynamically
Let's create a default Vue app using vue create your-app
and add the router plugin with vue add router
.
Then, create a folder within components
where you're going to put you components you want to include dynamically, let's say, dynComps
We need a specific folder because we don't want to include all the components present in the app dynamically.
After that, create this js
file within the components
folder:
import Vue from 'vue'
const requireComponent = require.context(
'./dynComps',
false, // Do not look in subdirectories
/[\w-]+\.vue$/
)
const list = []
requireComponent.keys().forEach((fileName) => {
const componentConfig = requireComponent(fileName)
const componentName = fileName
.replace(/^\.\//, '')
.replace(/\.\w+$/, '')
.split('-')
.map((kebab) => kebab.charAt(0).toUpperCase() + kebab.slice(1))
.join('')
list.push({
id: componentName.toLowerCase(),
label: componentName.charAt(0).toUpperCase() + componentName.slice(1)
})
Vue.component(componentName, componentConfig.default || componentConfig)
})
export default list
The above function looks within the dynComps
folder and load all the .vue
files, creating and returning a useful array we're going to use later.
Config the Router
Now we need to configure a bit our Router, here the relevant part:
const routes = [
{
path: '/',
component: Home
},
{
path: '/:id',
component: Home
}
]
In this bare-bones app there's just one view, Home
. The two routes are meant to render the listing part to show all of our components and the selected component if there's the fragment URL in the Router.
The listing part can be implemented easily with this code:
<template>
<div id="app">
<ul>
<li v-for="c in comps" :key="c.id">
<router-link :to="c.id">{{c.label}}</router-link>
</li>
</ul>
</div>
</template>
<script>
import list from '@/components/_globals'
export default {
computed: {
comps(){
return list
}
}
}
</script>
Render the component
Now, suppose the user reaches this URL: http://localhost:8080/compa
. From now on, we want to render the component CompA.vue
because the fragment compa
refers to it.
Vue.js comes with a generic component for that specific purpose:
<component :is="myCompDef"></component>
If myCompDef
is equal to ``CompA` then we're in business.
Here the whole Home
view that show both the list of available components as well as the business logic to intercept the Router fragment, find the corresponding component definition and render it:
<template>
<div id="app">
<ul>
<li v-for="c in comps" :key="c.id">
<router-link :to="c.id">{{c.label}}</router-link>
</li>
</ul>
<component v-if="comp" :is="comp.label"></component>
</div>
</template>
<script>
import list from '@/components/_globals'
export default {
computed: {
id(){
return this.$route.params.id
},
comps(){
return list
},
comp(){
return this.id ? this.getComp(this.id) : null
}
},
methods:{
getComp(id){
let ob = null
this.comps.forEach(d => {
if (d.id === id) ob = d
})
return ob
}
}
}
</script>
That's all, here the whole source code and you can see the app here.