1 of 49

Webpack

Anatomy of a Module Bundler

Tim Doherty

2 of 49

Who am I?

Tim Doherty

Software Architect at

Organizer, Santa Barbara JavaScript

Vice President, Paradise Dive Club

@TimCDoherty : linkedin.com/in/timdoherty : timdoherty.net

3 of 49

What is Webpack?

4 of 49

?

5 of 49

?

6 of 49

What is Webpack?

Webpack is a module bundler.

Webpack takes modules with dependencies and generates static assets representing those modules.

7 of 49

?

8 of 49

Modules

9 of 49

In the beginning, there were no modules...

Over the years, various solutions have filled the void:

  • IIFEs
  • JS module pattern
  • Revealing module pattern
  • Object “namespacing” (sub-modules)
  • Script tag vomit

https://www.flickr.com/photos/nicasaurusrex/1363221060

10 of 49

Then there were modules, but...

Module specs arose, but they were disparate:

  • CommonJS: synchronous, doesn’t work in browsers, adopted by Node.js/npm
  • AMD: asynchronous, designed for browsers, exemplified by Require.js
  • UMD: hybrid, creates CommonJS/AMD depending on context

11 of 49

Then there were native modules, but...

JavaScript finally got native modules with the arrival of ES2015, but...

  • A loader spec didn’t make the cut
  • A reference implementation (ES6 Module Loader) filled the void
  • JS devs saw that it was good, and built SystemJS on top
  • But still there was no loader spec

12 of 49

Meanwhile, the Module Bundler Arose

  • Parsing / traversing / fetching the dependency tree at runtime is costly
  • Concatenating resources is an accepted best practice
  • Module bundlers combine these two tasks

13 of 49

Why Webpack?

14 of 49

Why Webpack?

Why another module bundler?

  • RequireJS+r.js
  • Browserify
  • JSPM
  • Rollup
  • Dynapack
  • Lasso
  • etc.

15 of 49

Why Webpack?

  • Code-splitting and bundle optimizations for large applications
  • Loaders, which transform other resources into JavaScript
  • Clever parsing / expressions in dependencies
  • Extensible via plugin system
  • Hot Module Replacement (HMR)
  • All the cool kids are using it

16 of 49

Basics

17 of 49

Basics

Webpack takes one or more entry points as input, traverses their dependency trees, and produces one or more output bundles, or “chunks.”

  • (Pre/post) loaders can transform dependencies as they’re loaded
  • Bundles can be loaded asynchronously (code splitting)
  • Plugins can be used to process the resulting bundle(s)

18 of 49

Basics

feature.js:�console.log('feature module');

entry.js:�console.log('entry requires ' + require('./feature'));

$ webpack ./entry.js bundle.js

19 of 49

Basics

bundle.js:

(function(modules) {� /** webpack runtime **/�})([� function (module, exports) {� /** entry.js **/� },� function (module, exports) {� /** feature.js **/� }�]);

20 of 49

Configuration

21 of 49

Configuration

The webpack CLI is concise and effective. For all but the simplest applications, however, a configuration file will make your life much easier.

The main config sections are:

  • Entry point(s)
  • Output
  • Plugins
  • Modules

22 of 49

Configuration

module.exports = {� entry: "./entry.js",� output: {� path: __dirname,� filename: "bundle.js"� },� plugins: [],� module: {� loaders: []� }�};

23 of 49

Debugging

24 of 49

Debugging

The “devtool” config option gives various levels of debugging support. Each represents a balance between debug experience and speed/performance.

  • eval
  • source-map
  • hidden-source-map
  • inline-source-map
  • eval-source-map
  • cheap-source-map
  • cheap-module-source-map

25 of 49

Loaders

26 of 49

27 of 49

Loaders

Webpack can only process JavaScript natively, which already has modules

  • CommonJS
  • AMD
  • UMD
  • ES6 Modules

28 of 49

So, a build tool for my other resources?

29 of 49

Loaders

Webpack uses loaders to transform other resources into modules:

  • Stylesheets
  • Templates
  • Images
  • Fonts
  • etc.

This gives us a unified paradigm, in which every resource is a module.

In many cases this means that Webpack can, at least partially, replace build tools.

30 of 49

Loaders

At its simplest, a loader is just a function that takes a source file contents as a string or buffer, and returns a string or buffer.

module.exports = function(source) {� return source;�};

Produces:

function(module, exports) {� module.exports = '<source string>';�}

31 of 49

Loaders

More typically, loaders transform the source and, optionally, include runtime code

Take the css-loader, for example:

  • CSS files are read as strings, and wrapped in functions (modules)
  • A runtime module is required by the loader and injected into the bundle
  • The runtime inserts style tags for each resulting CSS module

32 of 49

Loaders

Inline:�var myCss = require('style!css!./styles.css');

By config:�loaders: [{� test: /\.css$/,� loaders: ['style', 'css']�}]

Both:�var myCssString = require('!!raw!./styles.css');

33 of 49

Loaders

Loaders are also commonly used to to transpile other languages to JS:

  • CoffeeScript
  • ES2015
  • JSX
  • TypeScript
  • Elm
  • etc.

34 of 49

Plugins

35 of 49

Plugins

Plugins can be used extend to Webpack’s functionality, and are typically used for post-processing the resulting bundle(s).

Much of Webpack’s “built-in” functionality is actually provided by bundled plugins:

  • Minification
  • Deduplication
  • Consolidating common code
  • Chunk optimization
  • Hot Module Replacement

36 of 49

Plugins

Plugins are instanceable objects with an “apply” method, inside which authors can hook into the webpack compiler internals.

function MyPlugin(options) {� // Setup the plugin instance with options...�}��MyPlugin.prototype.apply = function(compiler) {� compiler.plugin('done', function() {� console.log('This is my plugin.'); � });�};��module.exports = MyPlugin;

37 of 49

Plugins

Plugins are installed by adding instances to the webpack config plugins array

module.exports = {� ...� plugins: [� new webpack.HotModuleReplacementPlugin()� ]� ...�};

38 of 49

Code Splitting

39 of 49

Code Splitting

Webpack supports code splitting, or asynchronous dependency loading.

This allows you to optimize for large SPAs or multi-page applications.

  • Supports CommonJS and AMD style split-points*
  • Creates a separate chunk, which is loaded asynchronously

*webpack 2 will support System.import for code splitting

40 of 49

Code Splitting

var btn = document.querySelector('#my-btn');��btn.addEventListener('click', function (e) {� require.ensure(['./lazy'], function (require) {� var lazyModule = require('./lazy');� /** do something with lazyModule **/� });�});

41 of 49

Dev Server

42 of 49

Webpack Development Server

Wepack offers an ExpressJS-based development server, with middleware to serve your bundle(s), and Socket.IO to update the browser.

  • CLI, config file, or node API
  • Watches files and automatically refreshes your page

43 of 49

Automatic Refresh

iFrame mode - http://<host>:<port>/webpack-dev-server/<path>

  • No configuration change needed
  • Nice information bar on top of your app
  • Url changes are not reflected in the browser’s url bar (and vice versa)

Inline mode - http://<host>:<port>/<path>

  • Command line flag needed (“--inline”)
  • Status information in the browser log
  • Url changes are reflected in the browser’s url bar (and vice versa)

44 of 49

Hot Module Replacement (HMR)

45 of 49

46 of 49

Hot Module Replacement

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload. HMR is like LiveReload for individual modules.

  • Works with “inline” page refresh
  • Inserts an HMR runtime into your bundle
  • Opt-in, requires hooks in code
  • Bubbles up until hooks are found or the entry point is reached
  • Less intrusive with plugins that add hooks in post-processing

47 of 49

Demo

48 of 49

Questions?

49 of 49

Thanks!

@TimCDoherty

@sbjavascript