Let’s talk about our webpack config files

Anuj Nair
Tripping Engineering
6 min readJul 10, 2017

--

The magic of Webpack

Configuring webpack has proven to be challenging.

Every company and every project have different requirements, and configuring webpack to meet all of these needs, whilst still using best practices for serving assets and keeping the developer experience sane is not an easy task.

For this reason, we at Tripping.com want to share our production webpack config file. It’s certainly not perfect, but by sharing how we’ve configured this bundler, why we’ve chosen some of the configurations we have, and how we’ve gotten around some long standing issues, we hope to help share knowledge on webpack configuration for the community as a whole.

We hope other companies will share their webpack config files as well! #ourwebpackconfig

Tripping.com Requirements:

  • Use React & JSX for development, and transpile down to ES5.
  • Compile a version of our app for both server side and client side rendering.
  • No hot module loading for automatic incremental builds — this is our production config file only, so we don’t need HMR here 😊.
  • Use webpack code splitting to break our app our into different pages (home, search, details etc.), making the browser only download the JS & CSS it needs for that specific page it’s on.
  • Use code splitting to separate all vendor / node_module files into their own chunk — since we don’t change these often, the browser should be able to cache this file for long periods of time.
  • Compile individual CSS files for each individual split point, so the browser only downloads the CSS it needs to render the page it’s on.
  • Compile all SVGs into an optimized SVG sprite so we can download a single SVG resource.
  • Utilize browser caching to have the browser download only changed files on subsequent production releases by using a manifest file.
  • Make sure all assets are minified, compressed, and dead code is removed from the final compiled output (where applicable).

#OurWebpackConfig File

A more detailed explanation of each of the comments will be given in the next section. (Note: this is best viewed on desktop browsers since you’ll be able to see the config file and comments side by side)

Tripping.com webpack config file

[1] We’re creating a small reusable snippet for HappyPack here — see our detailed notes below as to why we do this.

[2] We have one entry point for our client JS, and one entry point for each page’s CSS file. Our server JS is generated in another config with server.js instead of client.js .

[3] Our main app is fingerprinted, as are all of the chunks we generate. This allows the browser to effectively cache files which don’t change for long periods of time.

[4] We list some helpful aliases/shortcuts here, and define what type of files we allow webpack to interact with (js, jsx, scss, svg etc.). Below we have all the loaders needed to interact with these files.

[5] PNGs and JPGs are loaded from their respective file — any image less that 8192 bytes is base64 encoded and inlined directly into our code to save on the network request.

[6] Some node modules need to be transpiled — we allow Babel to do that here.

[7] This is our main code which is transpiled.

[8] We extract all of our SVGs into a separate SVG sprite here, optimizing each individual one using SVGO, and then leaving an id references to them in our main client.js file so we can reference them through our custom svg-extractor.jsloader.

[9] We extract all of our SCSS into separate individual files defined by our entry points, run it through PostCSS to auto-prefix any selectors based on our minimum browser requirements, and make sure it is all minimized.

[10] Turn webpack module names into hashes instead of ids.

[11] Configure HappyPack.

[12] Extract CSS and SVGs into separate fingerprinted files.

[13] Extract common pieces of code into an app.js file …

[14] … and extract all of our node modules into a vendor.js file …

[15] … and extract the webpack configuration into a manifest.js file.

[16] Make sure loaders which use the LoaderOptionsPlugin are configured to minimize code and remove debug statements.

[17] Configure UglifyJS to remove dead code, minimize, and compress our JS.

[18] Load the manifest plugin so fingerprinted files can be mapped back to their original file names.

[19] Remove unused files due to multiple entry points and ExtractTextPlugin.

[20] Deterministically fingerprint file names.

A more detailed explanation for some of the points above

[1], [11] HappyPack is a loader/plugin for webpack which transforms multiple files in parallel. Before implementing HappyPack, we were seeing our webpack compilation time steadily increase over time, causing our development speed to slow down. Enabling HappyPack in our webpack config caused our build times to decrease by ~33%. It doesn’t guarantee speeding things up, so make sure you test build times when implementing this, but for us, we found it to be effective.

[2], [19] — The ExtractTextPlugin currently doesn’t allow us to extract CSS into different files per chunk. The only way to achieve this is to have multiple entry points in your webpack config, but in doing so, webpack produces a JS file for each corresponding entry point as well, regardless of the contents in each. For example, my webpack config above produces the following output:

                     ...
chunk-search.62abfcf.js 482 kB ...
chunk-details.28d9fd3.js 382 kB ...
chunk-home.acc51d7.js 175 kB ...
vendor.ea5eb93.js 1.11 MB ...
app.7e192e7.js 394 kB ...
search.b2328be.js 94 bytes ...
home.b2328be.js 94 bytes ...
details.b2328be.js 95 bytes ...
...

Webpack is creating our chunk files, app, and vendor files as expected, but it’s also producing a search.js , home.js and details.js file, each with the same fingerprint hash. When we open any one of these files up, we see the following:

webpackJsonp([15],{"+jSb":function(b,n){}},["+jSb"]);

Empty, minimized functions!

Since our application doesn’t reference these, and we don’t use them in any way, the ExtraneousFileCleanupPlugin helps us remove these useless files from our final build. This means the deploy package we eventually create is slightly smaller too.

[13], [14]— Extracting common chunks of code into a chunk, or extracting all vendor code via vendor code splitting, which can be shared across all of our pages is extremely beneficial to the end user. It means they don’t have to download modules which are shared across different chunks multiple times, it results in smaller chunks to download, and can even mean that we leverage browser caching across multiple webpack builds if a specific chunk doesn’t change.

[15] — We also extract the references that webpack has to its chunks into a separate file, so that we can inline this in the head of our application. This is called a webpack manifest, and is only used internally in webpack to reference other chunks. This file will always change when we change something in our application, so it’s important we extract it into a separate file, so we can leverage browser caching across all other chunks which do not change.

[3], [18] — Whilst webpack is producing all of its files, the Manifest Plugin calculates the hash of each chunk, and adds this to the filename, and to an external file called manifest.json. If a chunk’s content changes, the manifest plugin should output a new chunk hash, name the new file something different, and force the browser to download the new version of the file. This process is also known as fingerprinting. It’s very similar to the behavior in [15], but produces a manifest file which allows us to reference these chunks externally.

[20] — Finally, due to a long standing issue with webpack, we’ve written our own plugin called the AfterChunkHashPlugin, to rehash assets after they have been emitted, to ensure correct browser cache busting. A more detailed explanation of the issue we had to solve can be found in the readme of the AfterChunkHashPlugin repo.

That’s it!

We’d love to see how you’ve set up your webpack config files too! Please share with us via #ourwebpackconfig, or let us know if you have any other methods / techniques for optimizing your assets through webpack which we may have missed.

As always, if you’re interested in working with us, check out our open positions.

--

--