The 100% correct way to split your chunks with Webpack
By: David Gilbertson
Tags: JavaScript, Web, Webpack, Website, Web Development

Now, to talk about the other type of file-chopping…

Code splitting (don’t load code you don’t need to)

This particular approach only makes sense on some sites, says me.

I like to apply the 20/20 rule that I just made up: if there’s a part of your site that only 20% of users go to, and it’s bigger than 20% of your site’s JavaScript, then you should only load that code on demand.

Adjust those numbers to taste, obviously, and there’s more complex scenarios than that, obviously. The point is, there’s a balance, and it’s OK to decide that code splitting simply doesn’t make sense for your site.

How to decide?

Let’s say you’ve got a shopping site and you’re wondering if you should split out the code for the ‘checkout’, because only 30% of your visitors make it there.

The first thing to do is sell better stuff.

The second thing is to work out how much code is completely unique to the checkout. Since you should always do ‘bundle splitting’ before you do ‘code splitting’, you might already know how big this part of your code is.

(It might be smaller than you think, so do the sums before you get too excited. If you have a React site, for example, then your store, reducers, routing, actions, etc. will all be shared across the whole site. The unique parts will mostly be components and their helpers.)

So, you note that the code that is completely unique to your checkout page is 7 KB. The rest of the site is 300 KB. I would look at this and say meh, I’m not going to bother code splitting this, for a few reasons:

Loading it up front is no slower. Remember that you’re loading all these files in parallel. See if you can record a difference in the load times between 300 KB and 307 KB.

If you load this code later, the user will have to wait for that file after clicking on ‘TAKE MY MONEY’ — the very time at which you want the least friction.

Code splitting requires changes to your application code. It introduces asynchronous logic where previously there was only synchronous logic. It’s not rocket science, but it’s complexity that I think should be justified by a perceivable improvement to the user experience.

OK, that’s all the party pooper “this exciting tech might not apply to you” stuff.

Let’s look at two examples of code-splitting…

Polyfills

I’ll start with this because it applies to most sites, and is a nice simple introduction.

I’m using a bunch of fancy features in my site, so I’ve got a file that imports all the polyfills I need. It includes these eight lines:

I import this file right at the top of my entry point, index.js .

With the Webpack config from the bundle splitting section, my polyfills will be automatically split into four different files since there’s four npm packages here. They’re about 25 KB all up, and 90% of browsers don’t need them, so it’s worth loading these dynamically.

With Webpack 4 and the import() syntax (not to be confused with the import syntax), conditionally loading the polyfills is pretty easy.

Make sense? If all that stuff is supported, then render the page. Otherwise, import polyfills then render the page. When this code runs in the browser, Webpack’s runtime will handle the loading of those four npm packages, and when they’ve been downloaded and parsed, will call render() and things will carry on.

(BTW, to use import() , you’ll need Babel’s dynamic-import plugin. Also, as the Webpack docs explain, import() uses promises, so you’ll need to polyfill that separate to the other polyfills.)

Easy, right?

Here’s something a bit trickier…

Route-based dynamic loading (React specific)

Going back to the Alice example, let’s say that the site now has an ‘administration’ section, where sellers of products can log in and administer the crap that they have for sale.

This section has many wonderful features, plenty of charts, and a big fat charting library from npm. Because I was already doing bundle splitting, I could see that these were a shade over 100 KB all up.

Currently, I have a routing setup that will render <AdminPage> when the user is viewing an /admin URL. When Webpack bundles everything up, it’s going to find import AdminPage from './AdminPage.js' and say “hey, I need to include this in the initial payload”.

But we don’t want it to. We need to put that reference to the admin page inside a dynamic import, like import('./AdminPage.js') so that Webpack knows to load it dynamically.

It’s pretty cool, no config required.

So instead of referring to AdminPage directly, I could create another component that will be rendered when the user goes the the /admin URL. It might look something like this:

The concept is pretty straightforward, right? When this component mounts (meaning that the user is at the /admin URL), we’ll dynamically load ./AdminPage.js and then save a reference to that component in state.

In the render method, we simply render <div>Loading...</div> while we’re waiting for the <AdminPage> to load, or the <AdminPage> once it’s been loaded and stored in state.