Recently I started using Vue for a new admin panel on my board games website. And I noticed that my bundled JavaScript file started to grow fairly quickly. After only a few weeks the bundle had grown to approximately 1MB uncompressed. For my personal projects, I always want them to be as performant as possible without raising the cost. So, my mission today is to reduce the size of my JavaScript bundle and saving loading time.
Naturally, I am using Laravel for this project, so the packing is done using Laravel Mix on version 4.0.14 at the time of writing.
The first thing I wanted to know is where does all this code come from? A big chunk probably comes from the Vue framework, but this can’t account for an uncompressed bundle of 985kb. To find out what is filling your bundle webpack has a great tool called Webpack Bundle Analyzer which will create a nice view that shows you what code files are taking up the most space. Laravel mix also has a nice implementation of this plugin so I could add this without too much hassle.
The report I got back revealed that I had way too many libraries in the project, even though the project was only a few weeks old at that time.
The first thing that stood out to me was that the bundle included the full lodash library while I only used one function. So that was the first thing I changed. Lodash actually has support for importing only single functions from its core library, and that is what I did.
// Old import
import _ from 'lodash';
// New import
import _get from 'lodash/get';
This trick reduced the size of lodash in the bundle from 220kb to just 37kb. However, I was not satisfied. As stated before, I only used one function and only used it one time. So, is it really worth it importing a 37kb library for a single function? Of course not! So, I actually removed lodash all together and wrote the implementation for this case myself in only five lines of code.
This reduced the bundle size to about 710kb, and I was very happy!
Knowing that it was so easy to replace an entire library, I investigated the bundle report for any other library that could be removed. Now, this part might not be possible for all projects, but my project will only be used by me and a hand full of other users that use up-to-date browsers. Knowing this, I thought it had to be possible to remove the axios library and replace it with the fetch api.
Fetch API is a relatively new part of JavaScript. It’s a built-in replacement for XMLHttpRequest
that’s more flexible and has a better API. Having built my API requests the good way, I only had to replace axios in one file and this went pretty easy. All tests passed, and yet another 40kb had been removed from my JavaScript bundle.
At this point, the only big “library” that was left in my bundle was Font Awesome. I could not remove this library because those icons are just so great to use. However, the core library for using Font Awesome (excluding the icons) is 72kb in size. Which sounds like its a bit too much for just rendering SVG icons.
So, I started looking at how exactly the Vue component was built up and tried to make my own implementation that only did the basic things necessary to build up the SVG. However, this was easier said than done. Eventually, I got to a version where the SVG got loaded from the library and displayed on the screen. But the size was wrong and its position would change depending on the properties of the parent container. Also, the component I made had become overly complex and violated all rules of reactive programming.
I was glad I had tried this approach but was even more happy to go back to the component provided by FontAwesome. Optimizing is great, but it shouldn’t come at a development cost.
In the end, I stripped about 30% of code from my uncompressed JavaScript bundle which is much more than I expected. Most companies don’t bother to optimize this because computers and networks are fast enough to load and compile all this data. But more and more visitors will use these applications using mobile devices. For these cases, it can be very important to preserve mobile data and not have your device compile kilobytes of unused code.
I had a great time with this exercise and it had a very positive outcome, even so early in the development of this project. I will probably do this again at certain stages of development to keep my JavaScript bundles clean and remove unnecessary code.