Advertisement

#9 Step By Step: Split Chunks Plugin

In this article we will go over how to separate out the code that we write from the third party code that typically lives within the node_modules folder. We will start off by putting all of this third party code into a single bundle and we will end by seeing how we can take out specific parts and place them in their own bundle.

Add some packages

  • WebUi
    • package.json

In order for us to see how the split chunks plugin works we need to include some npm packages to add to our bundles. For demonstration we will add a couple of react packages and lodash (a). As usual the version numbers of the packages, as of this writing of this article, are shown in (b).

Terminal

npm install --save-dev
@types/lodash
@types/react
@types/react-dom
lodash
react
react-dom
(a) Command to add several npm packages so that we can see how to use the split chunks plugin.

package.json

{
    "devDependencies": {
        ...
        "@types/lodash": "^4.14.117",
        "@types/react": "^16.4.8",
        "@types/react-dom": "^16.0.7",
        ...
        "lodash": "^4.17.11",
        "react": "^16.4.2",
        "react-dom": "^16.4.2",
        ...
    }
}
(b) The version numbers of the packages we are adding as of the writing of this article.

Adding our own code

  • WebUi
    • Source
      • pages
        • page-1.page.tsx
        • page-2.page.tsx

In addition to third party code most application will also require code that we write ourselves. To demonstrate the splitting of code we are going to create two react pages that are very similar. The first (c) and second (d) both contain the same code with a little extra in page 1.

page-1.page.tsx

import * as React from "react";
import * as ReactDOM from "react-dom";

import { filter } from "lodash";

ReactDOM.render(
    <h1>Page 1</h1>,
    document.getElementById("root")
);

const examples = ["one", "two"];
console.log(filter(examples, (e: string) => {
    return e === "one";
}));
(c) The code for page 1 which includes a little extra to make us of lodash.

page-2.page.tsx

import * as React from "react";
import * as ReactDOM from "react-dom";

ReactDOM.render(
    <h1>Page 2</h1>,
    document.getElementById("root")
);
(d) The code for page 2.

Updating our webpack configuration

  • WebUi
    • webpack.config.js

The first changes to our configuration that we will make is to add the entry points for both of our pages (e). And since we are using react in our project we need to update our webpack configuration so that in addition to regular typescript files we are also passing react pages to the typescript loader.

webpack.config.js

...
const entry = {
    "page-1": ["./Source/pages/page-1.page.tsx"],
    "page-2": ["./Source/pages/page-2.page.tsx"]
};
...
const _module = {
    rules: [
        ...,
        {
            test: /\.tsx?$/,
            exclude: /node_modules/,
            use: [
                "ts-loader"
            ]
        }
    ]
};
...
(e) Modifying the ts-loader test so that both typescript and react files are passed to it.
Advertisement

With that done we are now ready to create our bundles using as usual the npm run build:prod command. When webpack is done we should see the output shown in (f). The importing thing to note is that the way the bundles are created now requires that all the necessary react code has to be present in both bundles. And of course the code needed for our lodash call is included in the page 1 bundle.

Output showing that we have create a bundle for each page. Both bundles contain all the necessary react code.
(f) Output showing that we have create a bundle for each page. Both bundles contain all the necessary react code.

Split chunks to the rescue

  • WebUi
    • webpack.config.js

There are several reasons that we would not like to have all of the react code in every bundle that we create. One of them for example is it severely impacts our ability to cache bundles. When compared to our code the third party code that we use is probably updated far less frequently but every time we update our code then a new bundle will be generated and then the user will have to re-download a lot of code that has not changed. To fix this we again need to update our webpack configuration file by adding a new optimization object (g).

webpack.config.js

...
const optimization = {
    splitChunks: {
        cacheGroups: {
            commons: { test: /[\\/]node_modules[\\/]/, name: "common", chunks: "all" }
        }
    }
};
...
module.exports = {
    ...
    optimization,
    ...
}
(g) Adding a new optimization object that contains the configuration for the split chunks plugin.

With that done we we once again create our bundles we should see the output shown in (h). What is happening here is that we have specified a test that looks for code that comes from the node_modules folder and if any is found it should be placed in a bundle with the name common.

We now have three bundles. Two for the code that we have written and a third for any code that lives in the
        node_modules folder.
(h) We now have three bundles. Two for the code that we have written and a third for any code that lives in the node_modules folder.

For the vast majority of projects that I have worked on this would be sufficient but every once in a while a little more splitting is required.

It does not have to be all third party in one bundle

  • WebUi
    • webpack.config.js

In addition to just splitting the code on whether it lives in the node_modules folder we can go further. Very rarely I have had the need to separate out a specific set of third part code which we can demonstrate here with the react code we are importing. What we want to do is keep our page 1 and page 2 bundles but instead of having a monolithic common bundle we will create a bundle for react and then a bundle for everything else in the node_modules folder (i).

webpack.config.js

...
 const optimization = {
     splitChunks: {
         cacheGroups: {
             react: { test: /[\\/]node_modules[\\/]((react).*)[\\/]/, name: "react", chunks: "all" },
             commons: { test: /[\\/]node_modules[\\/]((?!react).*)[\\/]/, name: "common", chunks: "all" }
         }
     }
 };
...
(i) Updating our split chunks configuration to create a react bundle and a common bundle for everything else.

Once again after re-creating our bundles we should see the output shown in (j).

Finally we have create one bundle for each page 1, page 2, react and lodash.
(j) Finally we have create one bundle for each page 1, page 2, react and lodash.
Exciton Interactive LLC
Advertisement