26 July 2022
6 mins read

Reducing the bundle

One package at a time!

Shashwat
Shashwat TheTrio

It’s hard to believe but today marks the halfway point of my GSoC project. I just finished filling out my evaluation form and soon enough it’ll be time for the final review.

But until that happens, we still have a lot of work to do. So let’s get started!

Note: This is my 4th Bi-Weekly blog. You can find the rest of them here

Summary

As always, here’s the list of PRs that I made during these past two weeks.

Issue(s) Description Pull Request Status
- convert course lists to JSX #5039 Merged
- remove lodash from bundle #5040 Merged
- split bundles based on routes #5050 Merged
- remove moment recur as a dependency #5053 Merged
#2319 migrate to date-fns #5065 Merged
- add Instructions for HMR #5038 Merged
- fix eslint config #5052 Merged
- don’t escape dashboard title #5060 Merged

And now, let’s go into a bit more detail.

More React Lists

This is mostly the continuation of my work mentioned in the last blog. I had already converted all campaign lists to React components and I have done the same for course lists. There are still some server-rendered lists remaining - about 4 in total if I’m not mistaken. But they are a bit more complicated and will require some more work.

Having only written React for so long, I was quite bored. So I decided that I would tackle the remaining server-rendered lists sometime in the future. I still expect to have a lot of time before the end of GSoC so hopefully, I can get this over with.

All in all, I converted 3 routes to React components. I also worked on getting the search bar on the explore page to work client side so that the initial page load is faster.

Lodash

lodash is a JavaScript library that provides functions that are used to manipulate arrays, objects, and other JavaScript types. Since Javascript has a rather limited standard library, lodash fills the gap.

lodash is however also famous for its bundle size coming in at around 70KB. Even when GZIPed, it still comes out at around 25KB.

For us, this problem was multiplied by the fact that we were also using lodash-es which provides ES6 exports and supports tree shaking.

Here’s the output from the Webpack Bundle Analyzer Plugin

Lodash

Not only is lodash one of the largest packages in the bundle, but it’s also included twice - once as lodash and once as lodash-es.

When I looked into it, I found that the culprit was just this single line in setOtherEditedArticles.js

import { includes } from 'lodash'

Just this one line was causing the entire package to be included in the bundle. Once I removed it, the bundle size decreased from 1.47MB to 1.42MB.

That might not sound like a lot but it’s almost a 3.5% decrease in bundle size. On the web, every byte counts.

Migrating away from Moment

moment is a JavaScript library that provides functions that are used to manipulate dates. Unfortunately, just like with lodash, it is also a large package. It comes in at around 300KB(and 72KB when GZIPed)

This is so bad in fact that we were already using a Webpack Plugin to remove unused locales from the bundle. But even then, as you saw from the graph above, moment was one of the largest packages in the bundle.

And this is not to even mention the fact that we have another date library in our bundle - date-fns. We didn’t use it directly but it was a dependency of chartkick

The first step to moving away from moment was to remove the moment-recur package from the bundle. Fortunately, that was easier than I expected since we weren’t using the capabilities of moment-recur in the first place.

Once that was done, replacing moment was relatively straightforward. I just had to replace the moment function calls with the date-fns equivalent. The problem was that since moment provided a wrapper around the native date object, finding where this wrapper was being passed and used was a bit of a pain. Unlike moment, date-fns operated on the native date object.

That being said, I think I have been able to do this correctly. There were some issues surrounding timezone since the native Date object only operates in local time, but I think I’ve been able to get away with it.

The bundle size has decreased from 1.42MB to 1.37MB and that brings the total size reduction to just under 7% from the original 1.47MB

Splitting the Bundle

Before this, we had just one bundle for all the routes. What this means is that even if the user is on the explore page, they have to download the code for every other route.

With dynamic imports, lazy loading, and Suspense, I was able to easily split the code for the routes into separate bundles. This is a lot more efficient than downloading the entire bundle on every page visit.

This doesn’t affect the overall bundle size but it should help with the initial page load.

What’s Next

For the first time, I’m feeling as if I’ve done almost everything major that I wanted to do.

There’s still work to do but most of it is pretty minor compared to everything I’ve done so far. Here’s what I have in mind presently

  1. Serving jquery only on routes that need it.
  2. Using fetch to make JSONP requests.
  3. Updating to the latest version of React-DND
  4. Converting some complicated class-based components to functional ones
  5. Converting more server-rendered lists to React components.

I’m sure more things will come up but for now, I’m pretty content with the current state of the project. I couldn’t have asked for more at the halfway stage.

And that’s about it. Have a great day!

Categories

GSoC GSoC-22