Webpack: Create Multiple Bundles Using Entry Points

7/5/2015webpack, javascriptCody ReichertBack to blogs

Webpack is module bundler that generates static assets for almost all of your front-end dependencies, and their dependencies. It's primary use-case is for creating optimized bundles for you Javascript, but it's quickly been extended to handle everything from fonts, images, and even a compilation step for CoffeeScript, LESS, and pretty much anything else you can think of.

A common use case for Webpack is single page applications, but another large one is for multi page applications. Loading a large JavaScript bundle on every page is not ideal, so let's set up Webpack to create multiple bundles for us.

A basic setup

So let's say the front-end JavaScript/Stylesheets structure of our site looks like this:

    └── static
        ├── dist
        └── src
            ├── js
            │   ├── Account.js
            │   └── Front.js
            ├── node_modules
            ├── package.json
            ├── stylesheets
            │   └── main.scss
            └── webpack.config.js

Most importantly, We have a main Javascript file for Front and Account.

The goal is to have Webpack generate a front-bundle.js and account-bundle.js bundle. The advantage here is that new visitors who aren't logged in don't need to load a huge JavaScript bundle just for visiting the homepage.

Single Entry Point

With a goal in mind, we can dig into Webpack and see what it offers. By default, Webpack makes you define an entry-point, basically the root JavaScript file for you app:

    module.exports = {
        entry: {
            app: "./static/src/app.js"
        },
        output: {
            path: "./static/dist",
            filename: "app-bundle.js"
        }
    };

Our site structure doesn't match up with that. With this, we would have to load all the account panel JavaScript on the homepage too - which is far from ideal.

Multiple Entry Points

Webpack supports multiple entry points for this reason. Here's a new configuration more suited to our site structure:

     module.exports = {
         entry: {
             front: "./static/src/js/Front.js",
             account: "./static/src/js/Account.js"
         },
         output: {
             path: "./static/dist",
             filename: "[name]-bundle.js"
         }
     };

Much better. What's happening here is that Webpack is now looking for both Front.js and Account.js, and will create a separate bundle, including the Webpack runtime and dependencies, for each of those. In the output object, we export the bundle to static/dist and use the [name] variable to name each bundle.

We end up with /static/dist/front-bundle.js and /static/dist/account-bundle.js. Great, so now we can the script tag to each page and we're done!

Almost...

Even though the bundles contain different code, there are a few libraries/modules that we use in both Front and Account. So, what about the use-case where a new user does end up logging in?

We wouldn't want to make them re-download the same JavaScript!

Common Chunks

While the solution above is good, it can be better.

Ideally, we have Front-bundle.js and Account-bundle.js - but we also have a Common-bundle.js that contains the modules we use everywhere. The browser will cache Common-bundle.js, so when a user transitions from the Front to the Account, they've already got most of what they need.

Say hello to the CommonChunksPlugin*.

Configuring CommonChunksPlugin

The common chunks plugin will look for and find all common modules and dependencies between your entry points, and automatically bundle them. All we need to is a little configuration:

      let commonsPlugin = new webpack.optimize.CommonsChunkPlugin(
          'commons',  // Just name it
          'common.js' // Name of the output file
                      // There are more options, but we don't need them yet.
      );

      module.exports = {
          entry: {
              front: "./static/src/js/Front.js",
              account: "./static/src/js/Account.js"
          },
          output: {
              path: "./static/dist",
              filename: "[name]-bundle.js"
          },
          plugins: [ commonsPlugin ]
          // more config options
      };

We initialize a new instance of the CommonChunksPlugin and pass a couple parameters (annotated). After that, the Webpack will do the rest.

The commons bundle will also be output to static/dist/, with the name that we gave it (common.js).

Wrapping Up

Now we're done! Remeber to add the <script> for both the entry bundle and the common bundle to the correct pages, and Webpack will do the rest.

It's a powerful tool, and I think does a great job of cleaning up the mess that is front-end dependency management. There's an endless amount of plugins and extentions already out there, so we'll see where Webpack ends up in 6 months to a year.

:: Cody Reichert

blog comments powered byDisqus