Webpack
Kaj Białas
Daniel Capeletti
Artur Kudeł
Michał Przyszczypkowski
01.12.2016
Presentation plan
-
What is Webpack?
-
How it works?
-
Options
-
Loaders (how it works?, how to write my own, common loaders)
-
Plugins (how it works?, how to write my own, common loaders)
-
Common scenarios (code splitting, multiple entry points, source maps ...? )
What is Webpack

How does it work?
-
An ES2015 import statement
-
A JavaScript require() statement
-
An AMD define and require statement
-
An @import statement inside of a css/sass/less file.
-
An image url in a stylesheet (url(...)) or html (<img src=...>) file.
What is a webpack Module

Graph dependencies
http://webpack.github.io/analyse
--profile --json >> stats.jsonCode example
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(2);
__webpack_require__(3);
alert('main');
/***/ },
/* 2 */
/***/ function(module, exports) {
alert('coins');
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(2);
alert('helpers');
/***/ }
/******/ ]);What about Grunt, Gulp…?
Differences:
- not using global scope
- not duplicating code while importing module
Why should we use webpack?
- code splitting
- performance
- optimazations
- loaders
- plugins
Code splitting
For big web apps it’s not efficient to put all code into a single file
Optimazation
-
Deduplication
-
Minimize
-
Chunks
What we need?
npm install -g webpack
webpack ./entry.js bundle.jsWays of configuring
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style!css" }
]
}
};webpack config file
Pure CLI
webpack <entry> <output>Webpack CLI options
Development shortcut -d
Equals to --debug --devtool source-map --output-pathinfo
Production shortcut -p
Equals to --optimize-minimize --optimize-occurence-order
Watch mode --watch
Watches all dependencies and recompile on change.
Display options
- --progress
- --json
- --no-color
- --sort-modules-by, --sort-chunks-by, --sort-assets-by
- --display-chunks
- --display-reasons
- --display-error-details
- --display-modules
- --display-exclude
Options
Entry
entry: './src/index.js'- String - single entry point
entry: ['./src/index.js', './src/googeAnalytics.js']- Array - multiple independent entry points, one result bundle
Entry
entry: {
'main': './src/index.js',
'unsupported': './src/unsupported.js'
}- Object - multiple independent entry points & multiple result bundles
Output
output: {
path: __dirname + '/dist'
}- Path - bundle goes here
Output
output: {
filename: 'main.js'
}- Filename - bundle name
output: {
filename: '[name]_bundle.js'
}For multiple bundles:
entry: {
'main': './src/index.js',
'unsupported': './src/unsupported.js'
}Results in 2 files: main_bundle.js & unsupported_bundle.js
Output
output: {
publicPath: 'http://myCdn.com/'
}- Publicpath - updates URLs inside app (usually for production only)
.image {
background-image: url('./test.jpg');
}.image {
background-image: url('http://myCdn.com//test.jpg');
}Loaders
module: {
loaders: [{
<loader configuration here>
}]
}Plugins
plugins: [
new MyPlugin({options: 'some option'}),
...
]Resolve
resolve: {
...
modules: [
"node_modules",
path.resolve(__dirname, "app")
]
}- Modules - where to look for modules
- Extensions
resolve: {
...
extensions: [".js", ".json", ".jsx", ".css"],
}Webpack dev server
npm i webpack-dev-server- Small node.js express server
- It uses webpack to compile assets in-memory
Installation
Running
webpack-dev-server --content-base /buildInline and iframe mode
HOT MODULE REPLACEMENT
Proxy
{
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}
}
}Loaders
What's this?
Loaders
How to install new loaders?
Loaders
via NPM
npm install --save-dev babel-loaderLoaders
How to include loaders?
Loaders
loaders: [{ test: /\.js$/, loader: 'babel-loader' }]Include loaders by config.
module.exports = {
entry: './main.js',
output: {
path: __dirname + '/dist',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader' }
]
}
};Loaders
Include loaders by require.
require("babel-loader!./main.js");require("!style!css!less!bootstrap/less/bootstrap.less");Loaders
Loaders examples.
Loaders
Loaders examples.
- file-loader
- css-loader / less-loader / sass-loader
- style-loader
- autoprefixer-loader
- babel-loader
Loaders
More loaders.
https://webpack.github.io/docs/list-of-loaders.html
Loaders
Loaders and preloaders.
Loaders
Loaders and preloaders.
module.exports = {
entry: './main.js',
output: {
path: __dirname + '/dist',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader' }
],
preLoaders: [
{ test: /\.js$/, loader: 'babel-hint-loader' }
]
}
};Loaders
Queue for loaders and preloaders.
- preloaders specified in the configuration
- loaders specified in the configuration
- loaders specified in the request (e.g. require('raw!./file.js'))
- postLoaders specified in the configuration
Loaders
Other parameters
Loaders
exclude, query
Loaders
exclude, query
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'jshint-loader'
}
],
loaders: [
{
exclude: /node_modules/,
loader: 'babel-loader',
query: {
cacheDirectory: true,
presets: ['react', 'es2015']
}
}
]
}Loaders
Can I create my the best custom loader?
Loaders
Yes!
Loaders
var _ = require('lodash');
var utils = require('loader-utils');
function processQuery(source, query) {
if (!_.isUndefined(query.search) && !_.isUndefined(query.replace)) {
if (!_.isUndefined(query.flags)) {
query.search = new RegExp(query.search, query.flags);
}
source = source.replace(query.search, query.replace);
}
return source;
}
module.exports = function (source) {
this.cacheable();
var query = utils.parseQuery(this.query);
if (_.isArray(query.multiple)) {
query.multiple.forEach(function (subquery) {
source = processQuery(source, subquery);
});
} else {
source = processQuery(source, query);
}
return source;
};What is it?
Plugin
Third party code that have access to Webpack chunks
Are they any good?
Plugin
- Yes. They enable the developers to inspect the chunks and make changes when needed
- Allows us to modify the files in runtime, so we can adjust them to run in diverse environments
- They are not that complicated to implement
- There are numerous different plugins available
Some plugins - You’ve probably used one of them before
- html-webpack-plugin // Helps in the creating of HTML files with hash support
- webpack-spritesmith // Webpack plugin that converts set of images into a spritesheet and SASS/LESS/Stylus mixins
- webpack-hot-middleware // Receive updated bundles from Webpack server
But watch out for outdated plugins!

How to use a plugin
First require your plugin
var SpritesmithPlugin = require('webpack-spritesmith');Then add it to plugins array
// ...
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl')
},
apiOptions: {
cssImageRef: "~sprite.png"
}
})
]
// ...Can I write my own plugins?

Important things
- Compiler
- Compilation
- Prototype.apply
function HelloWorldPlugin(options) {
// Setup the plugin instance with options...
}
HelloWorldPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function() {
console.log('Hello World!');
});
};
module.exports = HelloWorldPlugin;Example
function MyPlugin() {}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
// Explore each chunk (build output):
compilation.chunks.forEach(function(chunk) {
// Explore each module within the chunk (built inputs):
chunk.modules.forEach(function(module) {
// Explore each source file path that was included into the module:
module.fileDependencies.forEach(function(filepath) {
// we have learned a lot about the source structure now...
});
});
// Explore each asset filename generated by the chunk:
chunk.files.forEach(function(filename) {
// Get the asset source for each file generated by the chunk:
var source = compilation.assets[filename].source();
});
});
callback();
});
};
module.exports = MyPlugin;example
Common usecases
Problem:
I want to Uglify my code
new webpack.optimize.UglifyJsPlugin({
compress: true,
mangle: true,
comments: false
})Problem:
Styles are bundled with all other assets. I want to have a separate styles.css file.
var ExtractTextPlugin = require("extract-text-webpack-plugin");
...
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract({
loader: "css-loader"
}) }
]
},
plugins: [
new ExtractTextPlugin("styles.css")
]Problem:
I have different build configurations for local development and for production. For each one of them, I want to include a different file every time I require 'config' module in my JS code.
resolve: {
alias: { config: "/absolute/path/to/config.js" }
}require("config"); -> /absolute/path/to/config.js
resolve: {
alias: { config: "/some/dir" }
}require("config/file.js") -> /some/dir/file.jsProblem:
I have different build configurations for local development and for production. For each one of them, I want to have other global variables defined.
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
ENABLE_LOGGING: JSON.stringify(true)
})
if (ENABLE_LOGGING) {
console.log('Log some info here.')
}Problem:
I have multiple output files produced from couple of entry points, but there are some modules that are used in more than 1 output bundle. How can I avoid duplication of this module?
plugins: [
new CommonsChunkPlugin("common.js")
]plugins: [
new CommonsChunkPlugin({
filename: "common.js",
minSize: 50000 // bytes,
minChunks: 3
})
]Webpack - devtalk
By Michał Przyszczypkowski
Webpack - devtalk
- 463