To get started, you will need to generate a log file using webpack and the
--json parameter. You should also include the
--profile parameter to add timing information, and the
--progress parameter to display the status of the build process.
In case you’ve previously modified the webpack stats setting, make sure it’s set to
We’ll cover how to further optimize each parameter later on. So for now, the full command will look like so:
webpack -p --json --progress --profile > webpack-build-log.json
For an ad-hoc solution to get the history of log files, you can run the following:
webpack -p --json --progress --profile > "webpack-build-log.`date +%Y-%m-%d_%H-%M-%S`.json"
Note: Is your JSON valid? To make sure the build process does not output any unwanted information, you can use
babel-preset-env and its
We have generated a log file! What’s next?
Now that we have a log file, we can pass it to various services that will, in turn, provide you with aggregated information about your build process. One of those services is the official analysis tool.
This online version supports drag and drop, but don’t worry—the log file itself will not leave your machine. You can also find more tools in the official docs.
Our tool of choice: webpack-bundle-analyzer
For bundle optimization, webpack-bundle-analyzer happens to be the most useful. It is also widely used within the community.
You can use the webpack-bundle-analyzer as a standalone command line tool, or as a plugin within your webpack config. The tool will display a dependency graph using a treeview, visualizing the relative sizes of every file in your bundle.
Note: Make sure to pass the directory of your generated bundle as second parameter to get the real, parsed, and gzipped size of every file.
The file-upload-example app in all its unoptimized glory looks like this in the bundle-analyzer:
webpack-bundle-analyzer webpack-build-log.json dist/app
And will result in a diagram similar to this:
Our (intentionally unoptimized) app has a whopping 1.69MB stat size. The app size is 1.76MB parsed and 399.17KB gzipped. Lighthouse says it would take ~3200 ms to the first meaningful paint on a bad 3G connection.
This is far from good, but it’s the perfect starting point for what we’re about to do!
A quick look reveals what this bundle is made of:
- A lot of comments and whitespace because we “accidently” skipped minification
- moment.js with all it's locales: 453.34KB pure code / 90.28KB gzipped
- contentful-management as single bundle: 313.08KB pure code / 57.26KB gzipped
- babel-polyfill with: 225.91KB pure code / 48.92KB gzipped
- Also, we can spot multiple occurrences of lodash in the bundle
Let’s continue and get this monster into shape with our healthy webpack bundle diet.
The in-house way of putting your bundle on a diet:
First of all, I want to highlight that webpack has improved greatly. Version 2, and the fairly new version 3, both improved their bundling abilities. Some people say it’s a 50% reduction of the original build time when using version 3.
The webpack project is also backed by many companies, and the fact that some brilliant people now have time allocated by their employer to improve it—new versions get released almost daily.
The fastest way to reduce the payload is to run webpack with the
-p switch. This will enable the most basic production settings:
-p switch alone significantly reduced the bundle size. This will have a roughly 30% improvement on the first meaningful paint on a low-end mobile phone.
|Webpack comparison||Stat size||Parsed size||Gzipped size||First paint on 3G and low-end mobile|
|intentionally unoptimized||1.69MB||1.76MB||399.17KB||~ 3292 ms|
|webpack -p||1.65MB||640.45KB||208.79KB||~ 2276 ms|
Running webpack with the
-p switch will carry out the following tasks:
- Minification using the UglifyJsPlugin
- Runs the LoaderOptionsPlugin (see its documentation)
- Sets the Node.js environment variable to
productiontriggering certain packages to compile differently
Sometimes, executing webpack using only the
-p switch works well enough. However, it is good to know that the
-p switch does not optimize the following:
- The Node.js environment variable will not be set to
productionin webpack.config.js. If you rely on this setting in your config, you still have to prepend the webpack command with
- CSS created by css-loader is not minimized by default, so you probably want that in production
- Extracting css into its own file is more effective and caches better. You can use the extract-text plugin for that.
- Finding the correct source map setting for your needs is essential. We use the quick
cheap-eval-source-mapin development, and
falsefor no source map at all in production.
We have barely scratched the surface of what you can do to bring down your bundle size. By just running webpack the
-p flag, we have managed to drastically reduce our bundle payload. In our next post, we will run a production-like build to achieve an even better bundle size reduction.
Remember, lessening the payload has a number of advantages. By keeping things small, you decrease the risk of losing customers over a slow user experience—especially when they are using budget hardware through a less than ideal internet connection.