webpack seems to have a reputation for being prohibitively complex. Maybe this was true for version 1.x. Maybe this is still true for enterprise or large applications.

But I was pleasantly surprised to find that setting up webpack 2 on a simple, personal project was really quite simple. I suspect that just using webpack would generally be easier on many projects than setting up a separate module loader, build tool (or more elaborate yarn/npm scripts), CSS preprocessor, and various optimization tools (e.g. UglifyJS, Rollup).

On Using webpack as a Module Loader

At first, I was just planning on using webpack as a module loader (i.e. I wanted to use import statements). This felt much simpler than setting up SystemJS (manually, at least — jspm is different).

The minimum steps involved would be to:

  • Install webpack
  • Create a webpack configuration file
  • Create a build script

Install webpack (and d3-request, in this example):

yarn add -D webpack
yarn add d3-request

Create your webpack.config.js:

module.exports = {
  entry: './app.js',
  output: {
    filename: 'bundle.js'
  }
};

Create build script in your package.json:

"scripts": {
  "build:dev": "webpack --config webpack.config.js"
}

import however you normally would in app.js:

import * as d3 from '../node_modules/d3-request';

function treasuryIoApiCall(query) {
  return d3.json(
    'http://api.treasury.io/cc7znvq/47d80ae900e04f2/sql/?q=' + query,
    treasuryCallback
  );
}

function treasuryCallback(res) {
  console.log(res);
}

Include your output file in index.html:

<html>
  <head>
    ...
  </head>
  <body>
    ...
    <script src="bundle.js"></script>
  </body>
</html>

On Using webpack’s Dev Server

webpack has its own dev server (installed separately). It has live reloading. It’s not required. webpack is presumably compatible with whatever other server you would want to use locally.

To install:

yarn add -D webpack-dev-server

To configure:

  devServer: {
    contentBase: resolve(__dirname, 'dist'),
  }

To serve:

"scripts": {
  "serve": " webpack-dev-server"
}

On Quick Optimization Gains with webpack

I think of a webpack “plugin” as a script you can download, install, and configure to extend webpack’s capabilites. I think of a “loader” pretty much the same way, except that loaders specifically handle (load?) certain modules in a certain way, or something.

I mostly found the plugins and loaders I used on various Egghead tutorials, though I also referred to the article Getting Started with webpack 2 on running my scss files through webpack.

I used webpack with different loaders and plugins to do the following things:

  • convert my Sass to CSS
  • minify my CSS
  • append my CSS to my index.html with a hash
  • handle es6
  • bundle vendor JavaScript modules separately from my the application code I wrote
  • minify my JavaScript
  • tree shake my JavaScript
  • Add the bundled JavaScript files to my index.html file with a hash

The current webpack configuration of my project at importtreasury.info looks like this:

const webpack = require('webpack');
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  entry: {
    app: './index.js',
    vendor: ['flatpickr']
  },
  output: {
    filename: 'index.[name].[chunkhash].js',
    path: resolve(__dirname, 'docs')
  },
  context: resolve(__dirname, 'src'),
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET',
      'Access-Control-Allow-Headers':
        'X-Requested-With, content-type, Authorization'
    },
    contentBase: resolve(__dirname, 'docs')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [['es2015', { modules: false }]]
        }
      },
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract({
          loader: 'css-loader?importLoaders=1'
        })
      },
      {
        test: /\.(sass|scss)$/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'sass-loader']
        })
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest']
    }),
    new ExtractTextPlugin({
      filename: 'css/[name].[chunkhash].css',
      allChunks: true
    })
  ]
};

Its package.json looks like this:

{
  "name": "daily-treasury-cash-ops",
  "version": "0.0.1",
  "main": "index.js",
  "repository": "https://github.com/mattbatman/daily-treasury-cash-ops.git",
  "author": "Matt Batman",
  "license": "MIT",
  "scripts": {
    "build:dev": "webpack --config webpack.config.js",
    "build:prod": "webpack --env.prod -p",
    "serve": " webpack-dev-server"
  },
  "dependencies": {
    "d3-array": "^1.2.0",
    "d3-axis": "^1.0.6",
    "d3-format": "^1.2.0",
    "d3-request": "^1.0.5",
    "d3-scale": "^1.0.5",
    "d3-selection": "^1.0.5",
    "d3-transition": "^1.0.4",
    "flatpickr": "^2.5.4",
    "normalize-scss": "^6.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.24.1",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.1",
    "css-loader": "^0.28.0",
    "eslint": "^3.19.0",
    "extract-text-webpack-plugin": "^2.1.0",
    "html-webpack-plugin": "^2.28.0",
    "node-sass": "^4.5.2",
    "sass-loader": "^6.0.3",
    "style-loader": "^0.16.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.2"
  }
}

Recommended Resources