Tips and tricks in using Webpack
Permalink
Failed to load latest commit information.
README.md Mention source-map-explorer (#11) Dec 28, 2016

README.md

Webpack tricks

Just a small catalog of Webpack tips and tricks I've learned. All of those tips and tricks concern Webpack 1. Webpack 2 has a different API, so some of those tips won't work there. A detailed guide about migrating to v2 can be found here.

Table of contents

Progress reporting

Invoke Webpack with:

--progress --colors

Minification

Invoke Webpack with -p for production builds.

webpack -p

Multiple bundles

Export multiple bundles by setting the output to [name].js. This example produces a.js and b.js.

module.exports = {
  entry: {
    a: './a',
    b: './b'
  },
  output: { filename: '[name].js' }
}

Concerned about duplication? Use the CommonsChunkPlugin to move the common parts into a new output file.

plugins: [ new webpack.optimize.CommonsChunkPlugin('init.js') ]
<script src='init.js'></script>
<script src='a.js'></script>

Split app and vendor code

Use CommonsChunkPlugin to move vendor code into vendor.js.

var webpack = require('webpack')

module.exports = {
  entry: {
    app: './app.js',
    vendor: ['jquery', 'underscore', ...]
  },

  output: {
    filename: '[name].js'
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendor')
  ]
}

How this works:

  • We make a vendor entry point and load it with some libraries
  • CommonsChunkPlugin will remove these libraries from app.js (because it appears in 2 bundles now)
  • CommonsChunkPlugin also moves the Webpack runtime into vendor.js

Reference: Code splitting

Source maps

The best source maps option is cheap-module-eval-source-map. This shows original source files in Chrome/Firefox dev tools. It's faster than source-map and eval-source-map.

const DEBUG = process.env.NODE_ENV !== 'production'

module.exports = {
  debug: DEBUG ? true : false,
  devtool: DEBUG ? 'cheap-module-eval-source-map' : 'hidden-source-map'
}

Your files will now show up in Chrome Devtools as webpack:///foo.js?a93h. We want this to be cleaner like webpack:///path/to/foo.js.

  output: {
    devtoolModuleFilenameTemplate: 'webpack:///[absolute-resource-path]'
  }

Reference: devtool documentation

CSS

It's complicated. TBD

Development mode

Want to have certain options only appear in development mode?

const DEBUG = process.env.NODE_ENV !== 'production'

module.exports = {
  debug: DEBUG ? true : false,
  devtool: DEBUG ? 'cheap-module-eval-source-map' : 'hidden-source-map'
}

Be sure to invoke Webpack as env NODE_ENV=production webpack -p when building your production assets.

Investigating bundle sizes

Want to see what dependencies are the largest? You can use webpack-bundle-size-analyzer.

$ yarn global add webpack-bundle-size-analyzer

$ ./node_modules/.bin/webpack --json | webpack-bundle-size-analyzer
jquery: 260.93 KB (37.1%)
moment: 137.34 KB (19.5%)
parsleyjs: 87.88 KB (12.5%)
bootstrap-sass: 68.07 KB (9.68%)
...

If you're generating source maps (you should), you can also use source-map-explorer, which also works outside of Webpack.

$ yarn global add source-map-explorer

$ source-map-explorer bundle.min.js bundle.min.js.map

Reference: webpack-bundle-size-analyzer, source-map-explorer

Smaller React

React will build dev tools by default. You don't need this in production. Use the DefinePlugin to make these dev tools disappear. This saves you around 30kb.

plugins: [
  new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    }
  })
]

Be sure to invoke Webpack as env NODE_ENV=production webpack -p when building your production assets.

Smaller Lodash

Lodash is very useful but usually we only need a small part of its full functionality. lodash-webpack-plugin can help you shrink the lodash build by replacing feature sets of modules with noop, identity, or simpler alternatives.

const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

const config = {
  plugins: [
    new LodashModuleReplacementPlugin({
      path: true,
      flattening: true
    })
  ]
};

This may save you >10kb depending on how much you use lodash.

Requiring all files in a folder

Ever wanted to do this?

require('./behaviors/*')  /* Doesn't work! */

Use require.context.

// http://stackoverflow.com/a/30652110/873870
function requireAll (r) { r.keys().forEach(r) }

requireAll(require.context('./behaviors/', true, /\.js$/))

Reference: require.context