Tree-shaking and code-splitting are two techniques to reduce the size of JavaScript bundles in web applications. Code-splitting is bundling your code in a way so that it’s grouped into many small bundles that can be loaded as they are needed. Tree-shaking is the process of removing unused code during the bundle process.
The Civic Apps team has embraced the methods provided by React to implement code-splitting; namely Suspense and Lazy. Building our applications with Webpack now results in many small bundles instead of two larger bundles, for application code and application dependencies, and much improved loading times.
To further reduce the size of our application, we sought to tree-shake Lodash. Using the webpack bundle analyzer revealed that Lodash is typically one of the largest dependencies we have on our projects besides React and Leaflet. Lodash is ripe for tree-shaking because of its modular design. Also, we use it on nearly every project, but rarely use more than a few functions per project.
It’s worth noting up-front that bundling best practices are moving relatively fast. This blog post, like others, may quickly become out of date. When reading tutorials, it’s important to make sure that you are reading something relatively recent. During our attempt to implement tree-shaking in lodash, these blog posts, as well as this Webpack Github issue, proved to be very helpful. It’s worth reading the Webpack documentation on tree-shaking as well.
Takeaways
- Libraries are more suitable for tree-shaking than applications. This is because libraries generally have less dependencies than applications. If a dependency or sub-dependency of your application requires the complete lodash or is using syntax that doesn’t work with tree-shaking, you will get a tree-shaken version of lodash, as well as the version required by the dependency. Library authors should consider using a tree-shaking compatible lodash import syntax when possible so that the library does not bring along a full version of lodash.
- Tree-shaking is syntax-sensitive. To enable tree-shaking in lodash, you have to import functions using syntax like
import foo from 'lodash/foo'
.import { foo } from 'lodash'
will not tree-shake, nor will, obviously,import _ from 'lodash'
. Support for this syntax was implemented in Lodash v4. - If you are using Webpack v4, you don’t need to use lodash-es. The lodash-es library supports tree-shaking out of the box because it uses ES modules. However, with lodash v4, tree-shaking works without additional configuration in Webpack v4. It is also worth noting is that if you use lodash-es and you have other dependencies that require lodash, both will end up in the app bundle.
- Make sure to set the modules option to false if you are using Babel preset env plugin. By default, Babel rewrites modules to use CommonJS, which won’t tree-shake.
- The babel-plugin-lodash library essentially rewrites lodash import code to the above mentioned tree-shaking syntax for you. If you are already using that syntax, you don’t need this plugin.
- The lodash-webpack-plugin removes code in lodash that is not generally needed. However, if you need the code that is removed, your project will break. You can add a configuration file to tell the plugin what code to remove. However, this seemed unsustainable given that our needs may change over time.
- Using the lodash
_.chain
method will break tree-shaking. This is due to the way the _.chain function works, in it wraps its argument in an object that has access to a mostly complete suite of lodash methods using.map
,.filter
type syntax. Head here for more info.
In the end
We were successful in getting tree-shaking to work, but we actually ended up with a bigger application bundle. This is because we now have a tree-shaken version of lodash along with the full build of lodash.
We learned a lot in this process, and as more libraries move towards importing lodash in a tree-shaking compatible way, we’ll be in a good position to reap the benefits.