Using VueJS computed properties for dynamic module imports

Alex Scott
4 min readSep 1, 2017

If, like me, you appreciate the value of bandwidth and the impacts it can have on the speed and user experience of a website, you will obviously want to keep your bandwidth usage under control.

Using VueJS coupled with Webpack, this has been made exceptionally easy for you. Firstly, because VueJS & Webpack are really easy to use, especially with the Vue-CLI tool. But more importantly, is the fact the VueJS allows you to dynamically import modules and create components using computed properties.

Displaying Components

What really makes this easy is the way VueJS loads components. The ‘official’ method of inserting a component is to use the components name as the html tag. However, there is another method.

We can also insert components using a generic <component></component> tag and pass to it the is prop. So to load a component with called vue-accordion, we would put <component is="vue-accordion"></component>. We can also pass out props, as we normally would, to this ‘generic’ component wrapper.

Dynamic Component

Because VueJS allows us to insert components in the second method specified above, this means we can select the component based on a prop value.

As an example, if we have a prop named componentType, we can then load a component by binding the is prop of our child component to the componentType prop of our wrapper component. To do this we would insert our component like:

<component :is="componentType"></component>

This creates a mini component router of sorts. Below is an example Single File Component of what we would have so far.

<template>
<div class="componentWrapper">
<component :is="componentType">
</component>
</div>
</template>
<script>
export default {
name: 'component-wrapper',
props: {
componentType: {
type: String,
default: () => null
}
}
}
</script>

Dynamic Module Imports

By using VueJS dynamic components and computed properties, we can have VueJS only load the components ES module file when the component is used.

To do this, we need to make a small change to our component tag and add a computed property. Afterwards, our child components is prop will no longer be bound to the parents componentType prop. It will instead be bound to our computed property, for example componentLoader, which will in turn be bound to a parent prop, componentFile. This will effectively be a file for a VueJS Single File Component to load.

Our computed property is the place where we will dynamically load our component as a module. We can now go ahead and write a Single File Component, that will serve as a wrapper to dynamically load components module files, thanks to Webpack.

<template>
<div class="componentWrapper">
<component :is="componentLoader">
</component>
</div>
</template>
<script>
export default {
name: 'component-wrapper',
props: {
componentFile: {
type: String,
default: () => null
}
},
computed: {
componentLoader () {
return () => import('./${this.componentFile}
}
}
}
</script>

The Promise

But does it do promises, I hear you ask?

Well, the short answer is no, because Vue computed properties won’t resolve a promise, but component methods do return a promise. So, with a few small modifications to our Single File Component, we can have our component loader implement functionality that relies on promises.

Below is a modified Single File Component based on the one above, that includes some promise-based functionality. I’ve also expanded it to include the init code for when the component is first mounted and two extra props.

The first prop is for passing any options to the child component and the second is for passing content to the default slot of the child component.

As a side note, notice how I have passed all of my props as an array of options rather than separately. I would certainly recommend that you do the same, as it makes it easier to do stuff like this and above anything, it stops you having to type out all of them props and stops wasteful code.

I’ve also added comments to the Single File Component below, to help any beginners.

<template>
<div class="componentWrapper">
<component :is="componentLoader" :options="options">
{{ this.$props.slot }}
</component>
</div>
</template>
<script>
export default {
name: 'component-wrapper',
props: {
componentFile: {
type: String,
default: () => null
},
options {
type: Array,
default: () => []
},
slot: {
type: String,
default: () => null
}
},
mounted () {
if (this.$props.componentFile) {
loadComponent()
}
},
computed: {
componentLoader () {
return () => import('./${this.componentFile}
}
},
methods: {
loadComponent () {
this.componentLoader().then(comp => {
console.log(comp.data)
})
}
}
}
</script>

If you come up with any unique use cases for this, please do give me shout on Twitter or post in the comments. I hope you enjoy playing around with this.

The idea for this post came from a Tweet I seen earlier from Damian Dulisz, so thanks to him for that and definitely worth heading over to Twitter to give him a follow. Also worth checking out Sean T. Larkin as well, as without him and Webpack, this wouldn’t be possible.

--

--

Alex Scott

PHP & JavaScript focused developer, mainly using Laravel & Vue.JS, I develop most projects under the alias @codetheorist