In this article, I’ll demonstrate how you can build a lazy-load router with Vue.js in just a few lines of code. This will work natively in browsers that have implemented dynamic module imports, but I’ll also include a fallback for older browsers.
Grab the finished code here on GitHub.
You may also like:
Lazy Loading ES2015 Modules in the Browser.
If you’re using the latest version of any major browser, you can perform static import/export natively now. For Vue.js, this means you can export a component definition from a file like this:
And import it into your app like this:
If your components represent pages, it’d be better to fetch the module files on demand so that a user doesn’t have to download pages they don’t visit. Static imports are resolved at compile time, however. This means you can’t put
import BooksPage from './BooksPage'; inside an
if statement to conditionally load it. Instead, your static imports will start downloading as soon as the script that loads them runs.
componentis a built-in “meta” component that accepts a component definition through the prop
is. Like all Vue components, this can either be a component definition, or a Promise that resolves the component definition.
This code will work natively in the browser if you’re using the latest Safari or Chrome Canary, with other browsers soon to follow.
Creating a Tiny Lazy-Load Router
With that theory in mind, let’s make our lazy-load router. In your app template, create some anchor tags where the
href is the path to the component module for that page. Listen to the click event on these elements, prevent the redirect, and instead trigger a method,
We’ll define the
navigate method in the Vue instance and it will accept the click event as an argument. Use the value of the link’s
event.target.pathname, to dynamically import the required page component module and assign it to the local
page state property. This is dynamically bound to the
Note that versions of Vue.js before 2.5.0 need to include a
thencallback to correctly resolve the module defintion.
That’s it! If you run this in a browser that supports dynamic imports you’ll see this:
What about users who don’t have the latest version of Safari or Chrome? They’ll need a fallback. Let’s create one with Webpack.
Firstly, we’ll have to modify the
navigate method a bit. Webpack’s implementation of
import() requires it to know ahead of time which files it may need to dynamically load. You don’t need to specify the exact module name, just make sure the value you provide to
import() is a resolvable file or directory.
To do this, we’ll change the dynamic file name so it specifies the
pages directory and extracts the filename from the
Secondly, we need to put the comment
/* webpackChunkName: "pages/[request]" */ in the function so that Webpack knows to extract this chunk to a separate file. Otherwise, Webpack will bundle all the page component modules in one file and the benefits of lazy-loading will be forfeit.
You can use this simple Webpack config which has two notable features:
- Specifies a
chunkFilenameoutput property. This ensures the page component modules are named correctly in the Webpack output.
syntax-dynamic-importfor Babel to recognize the dynamic import statement.
Run Webpack with that config and you’ll get build files including:
- The main bundle will include Webpack’s implementation of dynamic import.
Checking for Dynamic Import Support
How do you tell the browser to use the fallback? As far as I know, there’s no specific way to check for browser support for
import(). My strategy is to use an inline script in the body of the document which creates a new
script tag and will conditionally change the source (either the main script or the fallback) depending on support for
import() requires it to know ahead of time which files it may need to dynamically load, the native implementation does not.
In practice, features like this will need to be progressive enhancements and will, therefore, require Webpack as a fallback, which reintroduces the same complications. Oh well, that’s web dev for you.