Weeding Out Your ES6 Webpack Bundle Sizes

Image source: iStock

Webpack, ES6, and little bit of Babel make a great tool set for building future ready JavaScript applications. Unfortunately, we still live in a day where browser support lags behind and end users are stuck on old versions. How nice would it be to write next generation JavaScript that ran anywhere? But the reality is we need transpilers to dumb down our code so browsers like IE9 can understand it.

That’s what Babel is: a tool to convert ES6 and beyond to good ole fashioned ES5. Nevertheless, you also need something to process this transpilation along with asset transformations like compiling SASS to CSS, running linting tasks, and packaging your static assets into code bundles. Webpack does just that. Yet unlike other bundlers, webpack scales well in larger applications mainly because of Code Splitting: the ability to chunk code into separate bundles so that assets can be managed and delivered as needed. At the end of the day, this process produces the JS, CSS, and other content files (bundles) your website or application depends on.

But remember, delivering fast loading applications and seamless content should not be sacrificed for technology choices. Even with great tools like Babel and webpack, your bundles sizes can quickly grow some persistent weeds unless you keep a close eye on the output. Let’s understand how these mishaps derive and why diligence is imperative.

Overconfidence (Allowing the Weeds)

A handful of developers, a great idea, and a couple months later, our dev team shipped its first public facing application built using ES6 and webpack. During the first few weeks of development, we gloated in the fact that by using ES6 imports, we could simply include the bits and pieces of a library that were really necessary. For instance, rather than including all of jQuery, we might only need to include the functionality needed such as the “classes” module for adding/removing CSS classes with legacy browser support. We also enjoyed the prod optimization plugins webpack provided for free. At any rate, we celebrated the minimal files sizes of our production build.

Back to Reality (Discovering the Weeds)

While testing in stage, all seemed fine and dandy. Gzip compression was turned on and any increase in bundle sizes went seemingly unnoticed. Let me note that we had two bundles: one being our source code (app), and the other being an extracted vendor bundle using the CommonsChunkPlugin.

However, the week of our soft launch and regression testing in an isolated testing environment told a different story. With gzip compression yet to be enabled, the question immediately arose:

Um… why are our JS file sizes huge?

The Problems (Identifying the Weeds)

During the course of development, we introduced two specific changes which seemed alright at the time.

  1. The first came about when IE11 started throwing console errors referencing `_toConsumableArray`. Under the hood, our version of webpack was using ES6 specific methods, and after searching Google and Stackoverflow, we opted to include babel-polyfill as a quick fix which many recommended.
  2. The second was when we opted to use a well-known library called node-uuid to generate UUIDs.

As it turned out, both of these additions had unforeseen results. Our app bundle shot up over 50kb and an extra 70kb was added to our vendor bundle. One may say another 50–70kb is no big deal in these days of high speeds. However, not only was our app loading both these bundles at once (over 120KB), but we were trying to optimize for mobile where every kb counts, and this certainly didn’t help.

The Solution (Picking the Weeds)

Solving our two issues above turned out to be rather simple. To start, we backed out babel-polyfill entirely and replaced it with the Array.from polyfill from core-js. Using ES6 modules, we imported the polyfill directly instead of whole library. Next, instead of bringing in an outside dependency, we reused a small method written in another project to produce UUIDs. With both problems solved, we shaved off almost the full 120kb that was originally added.

Practical Steps (Guarding Against Future Weeds)

  • You should know and document the minified and gzipped sizes of any third party libraries. If your team understands what you are adding to your bundles up front, the best decision can be made whether or not to include such dependencies. And, making sure additions are documented will be a lifesaver if or when you have to reverse them.
  • Prefer importing what you need rather than the whole library.
    For example, if you only need the chunk function from lodash, prefer importing/requiring the specific function rather than all of lodash as given below.
// Avoid
import _ from 'lodash';
// Prefer
import { chunk } from 'lodash/chunk';
  • Educate your fellow developers. Send emails, have conversations, put it up on a project wiki. Let the rest of the team understand bundlers like webpack, their pros/cons, and how to optimize bundle sizes. If you have a story like this one, let your team know why and how you fixed it.
  • Use a bundle analyzer tool. If you start to notice your output growing unexpectedly, a library such as webpack-bundle-size-analyzer can easily uncover what exactly is contributing to the size of your bundles.
  • Create environment specific webpack configs to optimize builds. For instance, in your dev config, you can enable everything you need for development and debugging including sourcemaps, dev servers, hot loaders, linters, and even build caches. But in the production config, use optimization plugins like DedupePlugin, and set `NODE_ENV = ‘production’` so development code doesn’t execute in prod and allows the minifier to remove dead code. I highly recommend reading through resources like SurviveJS’s webpack guide on this topic.
  • Consider using drop-in replacement libs for production bundles. For example, react-lite is an optimized implementation of React that has a minified size of 25kb compared to React’s 136kb. But beware, when using similar libraries, output needs to be thoroughly tested as coverage of the original library may not be 100%.
  • Document current and expected bundle sizes and communicate those with testers. QA is the final step in catching our development mistakes. They must be informed and trusted.

Wrap-up

Webpack alone is a lot to take on, and there are many optimizations to be aware of. It’s important to be proactive in properly maintaining output sizes and being prepared to quickly correct the mistakes that fall through the cracks. Don’t forget, spiffy downloads can be the difference between a user’s life being changed or missing the experience altogether. Let’s strive for the former.

Subscribe and Join Us!

Thanks for reading about how we are doing things here at LendingTree Engineering. If you enjoyed our story please subscribe to our publication. If you would like to join and help shape our company, please visit careers.lendingtree.com and contact us. Follow us on Twitter: @Careers_LT