Code Splitting With Vue.js and Webpack
Learn how Vue.js and Webpack can be used to split a single page app into more optimally sized files that can be dynamically loaded.
Join the DZone community and get the full member experience.
Join For Freeone possible downside to bundling your single page app with webpack is that you can end up with a really big bundle file, sometimes several megabytes in size!
the problem with this is that a user must download the whole file and run it before they can see anything on the screen. if the user is on a mobile device with a poor connection this process could take quite some time.
code splitting is the idea that a bundle can be fragmented into smaller files allowing the user to only download what code they need, when they need it.
for example, looking at this simple web page, we can identify portions of the app that we don't need on the initial load:
what if we delayed loading these parts of the code until after the initial render? it would allow a user to see and interact with the page much quicker.
in this article, i'll show you how vue.js and webpack can be used to split a single page app into more optimally sized files that can be dynamically loaded.
you may also like: what's new in vue?
async components
the key to code splitting a vue.js app is async components . these are components where the component definition (including its template, data, methods, etc) is loaded asynchronously.
let's say you're declaring a component using the component
api, i.e. vue.component(name, definition)
. rather than having a definition object as the second argument, async components have a function. this function has two notable features:
- it's an executor for a promise, i.e. it has a
resolve
argument. - it's a factory function, i.e. it returns an object (in this case, the component definition).
vue.component('async-component', (resolve) => {
resolve({
template: '<div>async component</div>',
props: [ 'myprop' ]
});
});
async components are the first step for code splitting because we now have a mechanism for abstracting sections of our app's code.
dynamic module loading
we'll also need webpack's help. say we abstract our component definition into an es6 module file:
asynccomponent.js
xxxxxxxxxx
export default {
template: '<div>async component</div>',
props: [ 'myprop' ]
}
how could we get our vue.js app to load this? you may be tempted to try something like this:
xxxxxxxxxx
import asynccomponent from './asynccomponent.js'`;
vue.component('async-component', asynccomponent);
however, this is static and is resolved at compile-time. what we need is a way to dynamically load this in a running app if we want to get the benefits of code splitting.
import()
currently, it's not possible to dynamically load a module file with javascript. there is, however, a dynamic module loading function currently under proposal for ecmascript called import()
.
webpack already has an implementation for import()
and treats it as a code split point, putting the requested module into a separate file when the bundle is created (a separate chunk , actually, but think of it as a separate file for now).
import()
takes the file name as an argument and returns a promise. here's how we'd load our above module:
main.js
xxxxxxxxxx
import(/* webpackchunkname: "async-component" */ './asynccomponent.js')
.then((asynccomponent) => {
console.log(asynccomponent.default.template);
// output: <div>async component</div>
});
note: if you're using babel, you'll need to add the
syntax-dynamic-import
plugin so that babel can properly parse this syntax.
now when you build your project you'll notice the module appears in its own file:
another note: you can give a dynamically imported module chunk a name so its more easily identifiable; simply add a comment before the file name in the same way i've done in the above example.
dynamic component loading
bring the pieces together now: since import()
returns a promise, we can use it in conjunction with vue's async component functionality. webpack will bundle asynccomponent separately and will dynamically load it into the app via ajax when the app calls it.
main.js
xxxxxxxxxx
import vue from 'vue';
vue.component('async-component', (resolve) => {
import('./asynccomponent.js')
.then((asynccomponent) => {
resolve(asynccomponent.default);
});
});
new vue({
el: '#app'
});
index.html
xxxxxxxxxx
<div id="app">
<p>this part is included in the page load</p>
<async-component></async-component>
</div>
<script src="bundle.main.js"></script>
on the initial load the page will be rendered as:
xxxxxxxxxx
<div id="app">
<p>this part is included in the page load</p>
</div>
when main.js runs it will initiate a request for the async component module (this happens automatically because webpack's import()
implementation includes code that will load the module with ajax!).
if the ajax call is successful and the module is returned, the promise resolves and the component can be rendered, so vue will now re-render the page:
xxxxxxxxxx
<div id="app">
<p>this part is included in the page load</p>
<div>async component</div>
</div>
here's a diagram to help you visualize it:
single file components
the idiosyncratic way to achieve code splitting in vue, however, is to use the beloved single file component . here's a refactor of the above code using an sfc.
asynccomponent.vue
xxxxxxxxxx
<template>
<div>async component</div>
</template>
<script>
export default {
props: [ 'myprop' ]
}
</script>
this syntax for importing is even neater:
xxxxxxxxxx
new vue({
el: '#app',
components: {
asynccomponent: () => import('./asynccomponent.vue')
}
});
code splitting architecture
that's the technical part out of the way. the question, now, is how can you architect an app for code splitting?
the most obvious way is by page . for example, say you have two pages in your app, a home page and an about page. these pages can be wrapped inside components, home.vue and about.vue, and these can be the split points of the app.
but there are other ways, for example, you could split any components that are conditionally shown (tabs, modals, drop-down menus, etc.) or that are below the page fold.
for my next article, i'll explore some different code splitting architectures for a vue.js spa, so stay tuned!
further reading
Published at DZone with permission of Anthony Gore, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments