Advertisement

#8 Step By Step: Bundle Analyzer Plugin

If you are curious and open up a bundle built by webpack you will find as your application gets more and more complex that it is pretty hard to have a broad understanding of what is contained within it. On top of that you would have no idea of the overall size of the bundle or the size of the individual components that make it up. Knowing these things are very important for us to first decide what we really need to include and once we have decided that to make those things as small as possible. In order for us to gather this information we are going to use the webpack bundle analyzer plugin.

What do our bundles look like?

  • WebUi
    • package.json
    • webpack.config.js

We are going to start as a lot of our article do by installing an npm package using the command shown in (a). As usual the version number for this package is shown in (b).

Terminal

npm install --save-dev webpack-bundle-analyzer
(a) Terminal command that we can use to install the webpack bundle analyzer.

package.json

{
    ...,
    "devDependencies": {
        ...,
        "webpack-bundle-analyzer": "^2.13.1",
        ...
    }
}
(b) The version number of the webpack bundle analyzer at the time of the writing of this article.

Of course for us to make use of our newly installed package we need to modify our webpack configuration by adding it to our plugins array (c). As in previous articles we are pushing the plugin into our array instead of just including it in the initialization because we are going to modify how it is added in just a little bit.

webpack.config.js

...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
...
plugins.push(new BundleAnalyzerPlugin());
...
(c) Including an instance of the bundle analyzer plugin in our plugins array.

Now that the plugin is added if we run our npm run build command again once our bundle is built a browser window should be open automatically that shows something very similar to what is shown in (d). The diagram that is shown is called a Voronoi diagram and if you hover our mouse of a cell you get information about the size of the contents of that particular cell.

A Voronoi diagram of the contents of our bundle.
(d) A Voronoi diagram of the contents of our bundle.

The reason that we have so much code included within our bundle when all we really have for our application is a console statement is because we are analyzing a bundle which contains all the front end code needed to make hot module replacement work. We do not really care about all of this extra code. All we are concerned about is the amount of code that will be shipped to our end users. Our next step is to change when we add in the analyzer plugin.

Production size is all that matters

  • WebUi
    • package.json
    • webpack.config.js

Instead of having the bundle analyzer plugin added to our plugins array any time we build a bundle we only want it added when we are creating a production bundle. Our first step in achieving this is to add a new script to our package.json file that will set our environment to production and will use the default production settings of webpack by adding the -p flag (e).

package.json

{
    ...
    "scripts": {
        ...,
        "build:prod": "set NODE_ENV=production&&webpack --progress -p",
        ...
    },
    ...
}
(e) Adding a new script in our package.json file to kick off a production build.

Now that we can specify a build as a production build we can modify our webpack configuration to only include the analyzer plugin when we do so (f).

webpack.config.js

...
const isProd = mode === "production";
...
if (isProd) {
    plugins.push(new BundleAnalyzerPlugin());
}
...
(f) Now we will just add the bundle analyzer plugin whenever we create a production bundle.

With that done if we use the npm run build:prod command we should now see the diagram of our bundle look something like (g).

The diagram of our bundle no longer contains the code for hot module replacement.
(g) The diagram of our bundle no longer contains the code for hot module replacement.

Now we could just stop here since everything is working as it should be but over time it does get a bit annoying that whenever we create a production bundle a server instance is started that we need to kill and a browser window is open that we need to close. Let's fix this.

Advertisement

Only analyze when I say to

  • WebUi
    • package.json
    • webpack.config.js

Instead of including the bundle analyzer plugin for every production build we will instead create a new script specifically for that purpose (h). In this new script we are just setting a node environment variable, namely the BUNDLE_ANALYZE variable, which I made up to a value. In the script we have have written that it is equal to true but we do not really care what it is equal to just that it is defined.

package.json

{
    ...
    "scripts": {
        ...,
        "build:analyze": "set NODE_ENV=production&&set BUNDLE_ANALYZE=true&&webpack --progress -p",
        ...
    },
    ...
}
(h) Adding a new script which contains the setting of the BUNDLE_ANALYZE node environment variable.

Now instead of checking whether the bundle we are creating is a production bundle we will check to see if our BUNDLE_ANALYZE variable has been set (i).

webpack.config.js


...
const isAnalyze = typeof process.env.BUNDLE_ANALYZE !== "undefined";
...
if (isAnalyze) {
    plugins.push(new BundleAnalyzerPlugin());
}
...
(i) Adjusting our code so that we check and see if the environment variable has been set and if it has we will include the bundle analyzer plugin.

We are now ready and able to analyze the size and contents of our bundles to see if there are areas that we may be able to realize some savings by reducing and or eliminating things that we do not need.

How about an example

  • WebUi
    • package.json

To understand things it is often good to take a look at an example. This semi-contrived example is closely related to what I experienced working on a project. While working on the project I noticed that the size of a bundle was very high on the order of 4MB un-minified and un-gzipped. I was expecting it to be high but not that high. Towards the end of the project I decided it was time to see if there was anything that I could do about it. A significant part of the problem was a mistake that I made when importing icons from Font Awesome. The following example is not what was happening but it is close enough for use to see how the bundle analyzer plugin can be used to help us out.

We start by saying we would like to include some font awesome icons in our project and installing the needed packages from npm (j).

Terminal

npm install --save-dev
@fortawesome/fontawesome-svg-core
@fortawesome/free-solid-svg-icons
(j) Installing the font awesome packages that we need for our project.

For the overall purpose of this example the version numbers do not really matter but for completeness they are shown in (k).

package.json

{
    ...
    "devDependencies": {
        "@fortawesome/fontawesome-svg-core": "^1.2.2",
        "@fortawesome/free-solid-svg-icons": "^5.2.0",
        ...
    }
}
(k) The version numbers of the font awesome packages used in this example.

Does our project support tree shaking?

  • WebUi
    • Source
      • index-fa-all.ts
      • index-fa-single.ts
    • webpack.config.js

The next thing we need to do is to create two files, (l) and (m), so that we can have two different entry points in our webpack configuration which will then generate two different bundles.

index-fa-all.ts

import { library } from "@fortawesome/fontawesome-svg-core";
import { faAddressBook } from "@fortawesome/free-solid-svg-icons";

library.add(faAddressBook);
(l) This will work fine if our project is setup so that webpack can perform tree shaking.

index-fa-single.ts

import { library } from "@fortawesome/fontawesome-svg-core";
import { faAddressBook } from "@fortawesome/free-solid-svg-icons/faAddressBook";

library.add(faAddressBook);
(m) This will work fine regardless of whether or not our application is set up for tree shaking.

We of course need to modify our entry object to point to the two files that we just created (n).

webpack.config.js

...
const entry = {
    "index-fa-all": "./Source/index-fa-all.ts",
    "index-fa-single": "./Source/index-fa-single.ts"
}
...
(n) Modifying our entry object so that we generate two bundles from our test files.

With that all done if we now analyze the size of our bundles we will see a huge discrepancy between the two in terms of their overall size (o).

Depending on how your project is setup your bundles may not be created the way you think they are
        being created.
(o) Depending on how your project is setup your bundles may not be created the way you think they are being created.

The point of this is that you can use this particular example to help with your particular project but to show how using the bundle analyzer plugin we may be able to pin point an opportunity or two tow reduce the size of the bundles that are shipped to the end user.

Exciton Interactive LLC
Advertisement