An introduction to

by Fabian Ihl

What is webpack not?

Webpack is NOT a task runner

Will it replace my task runner?

Maybe...

What is webpack?

"Webpack is a module bundler for modern JavaScript applications"
(webpack.github.io)

It will resolve any kind of dependency ...

  • Scripts
  • Templates
  • Stylesheets
  • Assets
  • Fonts
  • fragment ...

and bundle them into a final build

It will transpile / process

  • TypeScript
  • CoffeeScript
  • ES6
  • Less
  • Sass
  • Stylus
  • Pug (fka. Jade)
  • ...

And do a lot more ...

Wait!

What is a module?

How do I declare dependencies?

A short detour about Modules

Supported are CommonJS, AMD and ES2015 imports

CommonJS

Referencing a module

var SomeModule = require('./some-module.js');
SomeModule.doAwesomeStuff();

"require" the module

Use it

Defining a module

module.exports = {
  doAwesomeStuff: function() {...}
}

limiting access

function somePrivateMethod() {...}

function doAwesomeStuff() {...}

module.exports =  {
  doAwesomeStuff: doAwesomeStuff
}

not exported

exported

Use closures and be strict!

(function() {
  'use strict';

  module.exports = {
    doAwesomeStuff: function() {...}
  }

})();

ES2015 module import

Importing

import {myMember} from 'my-module';

import {foo, bar} from 'my-module';

import {longModuleMemberName as shortName} from 'my-module';

Import one member

Import multiple members

Use aliases

Defining a module

function somePrivateMethod() {...}

export function doAwesomeStuff() {...}

See official documentation

Require other stuff

in javascript files

var data = require('someData.json');

Will load the json data and convert them to an object.

var template = require('template.html');

Will return the template as string or template funtion

require('prettyStyles.css');

Will inject the css as style block into the <head> section

Such files will usualy be bundled into the corresponding js bundle saving additional http requests.

Require in css files

.button-delete {
  background-image: url('./assets/deleteIcon.png');
}

Will...

  • Discover the png file
  • Copy it into the dist folder using a hash as name
  • Replace the url() with the calculated hash

The same works in templates

<img src=require("./assets/someImage.png")/>
< imagine funny gif here />

Minimal configuration

(webpack.config.js)
const path = require('path');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle[chunkhash].js',
    path: path.resolve(__dirname, 'dist'),
  },
};

(Yes, it's a module, too)

Your main script file as entry point.

Output config

Name of your final js bundle, with cache busting enabled

Output folder

Handling non js files using loaders

const path = require('path');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle[chunkhash].js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {test: /\.(jpe?g|png|gif|svg)$/i, use: ['file-loader']},
      {test: /\.css$/, use: ['style-loader', 'css-loader']},
    ],
  },
};

Loaders are defined in the module section, they tell webpack how to recognize and handle files.

Handling assets using the file-loader.

Loaders can be chained.

There are loaders for almost everything out there.

Special case

index.html

Entrypoint will always be a js file.

How do we tell wepack to handle our index template?

HtmlWebpackPlugin to the rescue
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle[chunkhash].js',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html', }),
  ]
};

Require the plugin

Plugin section

HtmlWebpackPlugin will ...

  • Copy index.html into the output folder
  • Inject all bundles as script tags

Running webpack

Build

$ webpack

Will look for webpack.config.js and build your app as configured

Development

$ webpack-dev-server

Will look for webpack.config.js and launch a dev server with auto reload

Chunks

Splitting bundles

const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: {
    'app': './src/app.js',
    'vendor': './src/vendor.js',
  },
  output: {
    filename: '[name]-[chunkhash].js'
    path: path.resolve(__dirname, 'dist'),
  },
  module: {...},
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor', 'manifest']
    }),
  ]
};

Define multiple entry points

Use the CommonsChunkPlugin to build chunks

This will create:

  • index.html
  • app-d3b256d536a4ea13be70.js
  • manifest-5d28af194df34fef3fc4.js
  • vendor-00d6787913610ea5ba02.js

Linting

module.exports = {
  entry: './src/entry.js',
  output: {...},
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          // eslint options (if necessary)
        }
      },
    ],
  },
};

Testing

Have a look at karma-webpack

https://github.com/webpack/karma-webpack

Full sample

For a full sample have a look at the config of this presentation:

(webpack.config.js)
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: {
    'main': './src/main.js',
    'vendor': './src/vendor.js',
  },
  resolve: {
    modules: [
      'node_modules',
      path.join(__dirname, 'src'),
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name]-[chunkhash].js',
  },
  module: {
    rules: [
      {test: /\.pug$/, use: ['pug-loader']},
      {test: /\.css$/, use: ['style-loader', 'css-loader']},
      {test: /\.styl$/, use: ['style-loader', 'css-loader', 'stylus-loader']},
      {test: /\.(jpe?g|png|gif)$/i, use: ['file-loader']},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: ['file-loader']},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: ['svg-url-loader?noquotes']},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: ['file-loader']},
      {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, use: ['file-loader']},
      {test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, use: ['file-loader']},
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: 'src/index.pug', }),
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor', 'manifest']
    })
  ],
  devServer: {
    host: '0.0.0.0',
  },
};
(webpack.production.js)
const webpack = require('webpack');
const merge = require('webpack-merge');
const base = require('./webpack.config');

module.exports = function(env) {
  return merge(base, {
    plugins: [
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify('production')
        }
      }),
      new webpack.optimize.UglifyJsPlugin({
        beautify: false,
        mangle: {
          screw_ie8: true,
          keep_fnames: true
        },
        compress: {
          screw_ie8: true
        },
        comments: false
      })
    ]
  })
}

Thank you

Questions ?


Official webpack docs:

webpack.js.org

View this presentation at:

akuryou.github.io/introduction-to-webpack

Clone it, fork it at:

github.com/akuryou/introduction-to-webpack