#8 Step By Step: Bundle Analyzer Plugin
Friday, August 31, 2018
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
package.json
{
...,
"devDependencies": {
...,
"webpack-bundle-analyzer": "^2.13.1",
...
}
}
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());
...
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.
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",
...
},
...
}
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());
}
...
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).
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.
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",
...
},
...
}
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());
}
...
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
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",
...
}
}
Does our project support tree shaking?
- WebUi
- Source
- index-fa-all.ts
- index-fa-single.ts
- webpack.config.js
- Source
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);
index-fa-single.ts
import { library } from "@fortawesome/fontawesome-svg-core";
import { faAddressBook } from "@fortawesome/free-solid-svg-icons/faAddressBook";
library.add(faAddressBook);
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"
}
...
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).
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.