While mobile-first approach becomes a standard and uncertain network conditions are something we should always take into consideration it’s harder and harder to keep your application loading fast. In this series I’ll dig deep into Vue performance optimization techniques that we are using in Vue Storefront and that you can use in your Vue.js applications to make them loading instantly and perform smooth. My goal is to make this series a full and complete guide on Vue apps performance.
Part 3— Lazy loading Vuex stores and single components — soon
Part 4— Lazy loading libs and finding smaller equivalents — soon
Part 5 — Making use of Service Worker cache — soon
Part 6— Prefetching
How Webpack bundling works?
Most of the tips in this series will focus on making our JS bundle smaller. To understand while it’s crucial first we need to understand how Webpack is bundling all of our files.
While bundling our assets Webpack is creating something called dependency graph. It’s a graph that links all of our files based on imports. Assuming we have a file called main.js specified as an entry point in our webpack config it will be a root of our dependency graph. Now every js module that we will import in this file will become it’s leaf in the graph and every module imported in this leafs will become their leafs.
Webpack is using this dependency graph to detect which files it should include in the output bundle. Output bundle is just a single (or multiple as we will see in the later parts) javascript file containing all modules from dependency graph.
We can illustrate this process like this:
Now when we know how bundling works it becomes obvious that the more our project will grow the biggest initial JavaScript bundle will be. The bigger it’ll be the longer it’ll take to download and parse it so user will see something meaningful later. The longer user will wait the more probable it is that he/she will leave our website.
In short words bigger bundle = less users. At least in most of the cases.
Lazy loading
So how we can cut off bundle size when we still need to add new features and improve our application? The answer is easy — lazy loading and code splitting.
As the name suggests lazy loading is loading parts of your application lazily. In other words — loading them only when we really need them. Code splitting is just splitting the app into this lazily loaded chunks.
In most cases you don’t need all the code from your Javascript bundle right after user visits your website. Even if we will have 3 different routes in our app no matter where user will end up he/she always needs to download, parse and execute bundle with all of them even though only one is needed. What a waste of time and energy!
Lazy loading allows us to split the bundle and serve only the needed parts so user is not wasting time to download and parse code that’ll not be used.
To see how much of the JavaScript code is actually used in our website we can go to the devtools -> cmd+shift+p -> type coverage -> hit ‘record’. Now we should be able to see how much of the downloaded code was actually used.
Everything marked as red is something that is not needed on current route and can be lazily loaded. If you are using source maps you can click on any file in this list and see which of it’s parts were not invoked. As we can see even vuejs.org has a huge room for improvement ;).
By lazy loading proper components and libraries we managed to cut off the bundle size of Vue Storefront by 60%!
Ok, we know what lazy loading is it’s and that it’s pretty useful 😉
It’s time to see how we can use it in our Vue.js application.
Dynamic imports
We can easily load some parts of our application lazily with webpack dynamic imports. Let’s see how they work and how they differ from regular imports.
If we will import JS module in a standard way like this:
// main.js
import ModuleA from './module_a.js'
ModuleA.doStuff()
It will be added as a leaf of a main.js in the dependency graph and bundled with it.
But what if we will need ModuleA only under certain circumstances like a response to the user interaction? Bundling this module with our initial bundle is a bad idea since it may not be needed at all. We need a way to tell our application when it should download this chunk of code.
This is where dynamic imports can help us! Now take a look at this example:
// invoked as a response to some user interaction
getModuleA()
.then({ doStuff } => doStuff())
Let’s take a quick look at what happened here:
Instead of directly importing module_a.js we created a function that returns theimport() function . Now webpack will bundle content of dynamically imported module into a separate file and unless the function will be invoked the import will not be called and file won’t be downloaded. Later in the code we downloaded this optional chunk of code as a response to some certain user interaction (like route change or click).
By making a dynamic import we are basically cutting off the leaf that will be added to the dependency graph and making the separate one that will be downloaded when we decide it’s needed (which implies that we are also cutting off modules that are imported inside module_a.js ).
Let’s see another example that will better illustrate this mechanism.
Let’s assume we have 4 files: main.js, module_a.js, module_b.js and module_c.js. To understand how dynamic imports work we need only source code of main and module_a:
By making module_a a dynamically imported module we are cutting part of the dependency graph with module_a and all it’s children. When module_a is dynamically imported it loads together with modules imported inside of it.
In other words we are just creating a new entry point for the dependency graph.
This how our dependency graph and bundles will look like with given setup.
Lazy loading Vue components
We know what lazy loading is and why we need it. It’s time to see how we can make use of it in our Vue application.
The good news is that it’s extremely easy and we can lazily load whole SFC along with it’s css and html with the same syntax as previously!
…that’s all you need! Now the component will be downloaded only when it’s requested. Here are the most common ways to invoke dynamic loading of Vue component:
Please note that the invocation of lazyComponent function will happen only when component is requested to render in a template.
For example this code:
<lazy-component v-if="false" />
will not dynamically import the component since it’s not added to the DOM (but will do it as soon as the value will change to true which is a nice way to conditionally lazily load Vue components)
Summary
Lazy loading is one of the best ways to make your web app more performant and cut off on it’s size. We learned how to use lazy loading with Vue components. In the next part of this series I’ll show you how to split your Vue application code with vue-router and async routes.
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish.AcceptRead More
Privacy & Cookies Policy
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.