Webpack

A module bundler

Why do we need this?

Webpack

The old way

Script includes

<script src="js/jquery.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/some-jquery-plugin.js"></script>
<script src="js/react.js"></script>
<script src="js/react-router.js"></script>
<script src="js/home.js"></script>

Webpack

Script includes

  • Have to know "correct" order (inter-dependencies)
  • Have to make sure correct dependencies are added
    (on each page)
  • Inefficient fetching (HTTP 1.1 concurrency limit)
  • No package management, meaning you have to copy/paste in prebuilt vendor code
  • No way of using new JavaScript features that requires transpiling (ES6-7)

Webpack

The improvement

Concatenation..

grunt.initConfig({
  concat: {
    files: {
      home: [
        'js/jquery.js',
        'js/jquery-ui.js',
        'js/some-jquery-plugin.js',
        'js/react.js',
        'js/react-router.js',
        'js/home.js',
      ]
    }
  }
});

Webpack

  • Still means you have to know the ordering of dependencies
  • What happens when you add a new page?

Concatenation

Concatenation

grunt.initConfig({
  concat: {
    files: {
      home: [
        'js/jquery.js',
        'js/jquery-ui.js',
        'js/some-jquery-plugin.js',
        'js/react.js',
        'js/react-router.js',
        'js/home.js',
      ],
      dashboard: [
        'js/jquery.js',
        'js/jquery-ui.js',
        'js/some-other-jquery-plugin.js',
        'js/react.js',
        'js/react-router.js',
        'js/dashboard.js',
      ]
    }
  }
});

Webpack

  • Re-downloading the vendor code for every page (no code-sharing)
     
  • Can be solved (somewhat) by separating the common from the page specific

Webpack

Concatenation

grunt.initConfig({
  concat: {
    files: {
      common: [
        'js/jquery.js',
        'js/jquery-ui.js',
        'js/some-jquery-plugin.js',
        'js/some-other-jquery-plugin.js',
        'js/react.js',
        'js/react-router.js'
      ],
      home: [
        'js/home.js',
      ],
      dashboard: [
        'js/dashboard.js',
      ]
    }
  }
});

Webpack

Concatenation

manual code sharing

Webpack

Concatenation

manual code sharing

  • But this means you have to know and manually separate the common from the page specific
     
  • This is easy when you only share vendor code, but hard when you share app code

Webpack

Concatenation



var Home = React.createClass({/* ... */});

Webpack

Main issues

  • Globals everywhere (namespace hell)
  • Difficult to reason about the definitions and use
  • Implicit coupling
  • Changing code order can cause unexpected issues
  • Hard to write tests without loading your entire app

Webpack

The solution

Modules

+

bundling

Webpack

Webpack

Webpack


var React = require('react');
var Home = React.createClass({/* ... */});

module.exports = Home;
  • Can use dependency management (npm)
  • Get to leverage npm (don't re-invent the wheel)
  • Every module declares their own dependencies, so the bundler can build the dependency graph
  • No more globals (unless you specifically declare them)
  • Explicit coupling
  • Everything always loads in the correct order
  • Enables you to test each module in isolation

Webpack

Webpack

Goals

Webpack

  • Code splitting and on demand loading
  • Low initial load time
  • Every static asset as a module
  • 3rd-party libraries as modules
  • Every part customizable
  • Suited for big projects

Webpack

Using it



> npm install webpack --save-dev


> webpack js/home.js build/bundle.js

CLI

Webpack

Using it

# terminal

> webpack

Config file

// webpack.config.js
module.exports = {
  entry: {
    home: 'js/home',
    dashboard: 'js/dashboard'
  },
  output: {
    filename: 'build/[name].js'
  }
};

Webpack

Plugins

(easing the load)

  • Automatically pull out common stuff to one or more separate files (code splitting)
  • Deduping (removing duplicate code)
  • Minification
  • Hot module replacement (this is crazy stuff!)

Webpack

Plugins



module.exports = {
  entry: {
    home: 'js/home',
    hashboard: 'js/dashboard'
  },
  output: {
    filename: 'build/[name].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('build/common.js')
  ]
};

Code splitting (common)

Plugins

<!-- home.html -->
<script src="build/common.js"></script>
<script src="build/home.js"></script>

Code splitting (common)

Webpack

<!-- dashboard.html -->
<script src="build/common.js"></script>
<script src="build/dashboard.js"></script>

Loaders

(easing the load)

  • The stuff that handles all your 'require' statements
  • Matches the require path and transforms the statement to appropriate content from the file

Webpack

Loaders

Webpack

// webpack.config.js
module.exports = {
  // ...
  module: {
    loaders: [
      { test: /\.js$/, loader: 'babel' }
    ]
  }
};

Loaders

Webpack


// js/home.js (with es6 and jsx)
const React = require('react');
let foo = <div>hello world</div>;
let add = (x, y) => x + y;

Loaders

Webpack


// bundle/home.js (bundled)
var React = __webpack_require__[0];

var foo = React.createElement(
  "div",
  null,
  "hello world"
);

var add = function add(x, y) {
  return x + y;
};

Debugging?

Webpack

Source maps



module.exports = {
  // ...
  devtool: 'eval' // 'source-map'
};

Styles


// js/home.js
require('style!css!less!../styles/home.less');

var React = require('react');
var Home = React.createClass({/* ... */});

module.exports = Home;

Demo

And there's more

  • Images
  • Fonts
  • Long-term caching (hashing file names)
  • AMD, CJS, ES6 Modules

Webpack

For more

http://webpack.github.io/docs/

Webpack

Questions?

Webpack

Introduction to Webpack (talk)

By Eirik Langholm Vullum

Introduction to Webpack (talk)

  • 3,279