Reducing JS Bundle Sizes Using Tree-shaking

This blog is intended for developers who are familiar with React development and wants to learn about how to reduce JavaScript bundle sizes of React applications using tree-shaking.

Problem Statement

When JavaScript bundles gets sent over network, they are usually compressed. While it reaches the browser, it is decompressed and parsed/compiled/executed.

A diagram illustrating the process of downloading, decompressing, parsing, compiling, and executing JavaScript.

Above illustration shows the JS bundle sizes as it being transferred from the server to client for processing. A compressed JavaScript bundle with 300 KB becomes 900 KB when decompressed and that bundle is then executed by the browser.  This point is important to note because as far as resource processing goes, the amount of JavaScript that browser needs to parse and execute is substantially larger than the compressed version. And JavaScript is costly to process than other resources such as html, css or images because they undergo through several stages such as parsing, compiling before execution. This is the reason why JavaScript developers must strive to reduce the code prior to bundling.

What is Tree-shaking?

Tree-shaking is a process of eliminating unused code during or before the bundle process. Most modern day web applications like React accumulate dependencies fairly quickly and if is not particularly coded with resource or performance in mind, unused dependencies may end up bloating the application. Tree-shaking technique aims to address this issue and ensures only executable code is sent to the client for processing. In this blog, I will demonstrate how to tree-shake unused dependencies using  a library called lodash and then automate this process using webpack module builder.

Lodash

Lodash is a JavaScript package which provides utility functions for more complex JavaScript operations using functional programming paradigm. For example, we can perform these operations:

  1. .pick() – returns an object with only specified properties
  2. .omit() – returns an object with specified properties removed
  3. .intersection() – returns an array with shared elements from n arrays

Below is a React component class that utilizes Lodash:

 

 


import React, { Component } from 'react';
import autobind from 'autobind-decorator';
import lodash from 'lodash';
class Home extends Component {
state = {
value: ''
};
@autobind
handleChange(e) {
this.setState({
value: e.target.value
});
}
render() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}
}
export default Home;

view raw

Home.js

hosted with ❤ by GitHub

In this example, we are simply importing the entire lodash package but have not any of its functions. Regardless, let us inspect how much space it occupies in our production build. I will be using the Webpack Bundle Analyzer webpack plugin to inspect the size of output files produced by webpack module builder:

Screen Shot 2020-02-11 at 7.00.01PM

Upon inspection, we can see lodash is one of the largest dependencies that our application uses besides React. Let us understand these size values:

  1. Stat size: original size of the module as-is before minification and gzip.
  2. Parsed size: size of the module after minification. If you are using a webpack plugin like UglifyJS, then this represents the minified version of the bundle.
  3. Gzipped size: size of the module after performing Gzip compression.

Since our application does not use any minification, parsed size is the number we should be keenly focused on. The file size reads 527 KB – substantially huge number, will potentially cause processing delays on browsers. Since lodash is built using a modular pattern, it is an ideal candidate for tree-shaking.

Tree-shaking Process

Tree-shaking process involves ECMAScript 6 modules. ES6 introduced built-in modules to JavaScript ecosystem so it is possible now to write classes or functions in their respective files and export and/or import these modules wherever needed using keywords importand export.

JavaScript dependencies are imported in our applications using static importstatement as follows:

import lodash from 'lodash';

What does it mean?

It means we are telling webpack to import everything from lodash, regardless of whether the application uses all of its functionalities or not. If our application only requires a single utility function, then this import statement is meaningless. This will result in substantially large bundle size as we saw earlier and end up increasing the client processing time. If our application does not need everything in the module, you can specify the modules names like so:

import isEqual from 'lodash/isEqual';

Re-bundling the application produces this:

Screen Shot 2020-02-11 at 6.51.39PM

The results are phenomenal because we are able to reduce the bundle size by almost 87% from 527 KB to 65 KB.

This process can be leveraged to reduce JS bundle sizes only if the packages adhere to modular pattern. Otherwise, I suggest looking forward toward other packages that may have similar functionalities and does not cause an overhead. All of a sudden, the responsibility now lies on the third-party vendors who owns the package to adapt to the modular pattern.

Automation possible?

Sometimes, it is possible to write code unknowingly and bring in unused packages in our application. We want our building tool to handle the tree-shaking process for us and cull the unused packages before it ends up in the final build. But unfortunately, tree-shaking process cannot be automated and developers have to proactively search through component classes and remove any unused package manually.

In a good note, however, we have a specific babel plugin to cull unused packages of lodash during the build process. To setup tree-shaking with lodash, utilize the babel-plugin-lodash dependency and configure with Babel as follows:

babel.config.js

{
  "plugins": ["lodash"],
  "presets": [...]
}

Conclusion

In this guide, we looked at how to reduce JS bundle sizes using tree-shaking technique. This can be leveraged if your application relies on dependency that may substantially increase the bundle sizes. Both developers and end-users benefit from this process so it is a good habit to start aiming to produce lean bundles.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s