Webpack is easier then you think
npm install webpack -g
The webpack command is now available globally. However, this is not a recommended practice. This locks you down to a specific version of webpack and might fail in projects that use a different version.
Install webpack (v2.x) locally
npm install webpack@2.2.0 --save-dev # yarn add webpack@2.2.0 --dev
userService.js
// a commonJS module
function userService() {
this.getById = function(id) {
return { id: 123, name: 'peter' }
}
this.getAll = function() {
return [
{ id: 123, name: 'peter' },
{ id: 222, name: 'robbert' }
]
}
}
module.exports = new userService();
main.js
var userService = require('./userService');
var users = userService.getAll();
users.forEach(function(user) {
console.log(user.id, user.name);
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack 101</title>
</head>
<body>
<h1>Webpack 101</h1>
<script src="bundle.js"></script>
</body>
</html>
A minimal config file
// webpack.config.js
module.exports = {
entry: './main.js', // app entry file
output: {
filename: 'bundle.js' // bundle output filename
}
};
A more complete config
// webpack.config.js
module.exports = {
entry: {
app: './main.js', // app entry file
vendor: './vendor.js', // vendor entry file
},
output: {
path: __dirname + '/dist' // output folder
filename: 'bundle.js', // bundle output filename
publicPath: "/dist/" // path used inside bundle file
},
module: {
rules: [...], // rules how to process files
},
plugins: [...] // additional plugins
};
Via command line (when installed globally)
webpack // for building once for development
webpack -p // for production (minification)
webpack --watch // watch file changes and rebuild
webpack -d // include source maps
Via npm (when installed locally)
"scripts": {
"build": "webpack"
}
Open your app with serve or live-server
$ live-server
Serving "/Users/me/git/vue-webpack" at http://127.0.0.1:8080
// ./userService.js
function UserService() {
this.getById = function(id) {
return { id: 123, name: 'peter' }
}
this.getAll = function() {
return [
{ id: 123, name: 'peter' },
{ id: 222, name: 'robbert' }
]
}
}
export default new UserService()
// ./main.js
import userService from './userService'
var users = userService.getAll()
users.forEach(function(user) {
console.log(user.id, user.name)
});
It just works
Install
npm install jquery --save # yarn add jquery
Add some html
<ul id="list" />
and use it
// main.js
import $ from 'jquery';
import userService from './userService'
var users = userService.getAll()
users.forEach(function(user) {
$('#list').append(`<li>${user.name}</li>`);
});
When running webpack we can see jquery is bundled with our own code.
$ webpack
Hash: f7e4f8b006ab65596006
Version: webpack 2.2.0-rc.3
Time: 295ms
Asset Size Chunks Chunk Names
bundle.js 271 kB 0 [emitted] [big] main
[0] ./~/jquery/dist/jquery.js 267 kB {0} [built]
[1] ./userService.js 621 bytes {0} [built]
[2] ./main.js 227 bytes {0} [built]
No need to start your own web server
The Webpack-dev-server combines automatic refresh (after bundle rebuild), faster bundeling and hot module replacement.
Setup
# install
npm install webpack-dev-server@2.2.0 --save-dev
// package.json
...
"scripts": {
"build": "webpack",
"serve": "webpack-dev-server --open"
}
// webpack.config.js
module.exports = {
output: {
filename: 'bundle.js',
publicPath: '/' // required for webpack-dev-server
},
...
devServer: {
historyApiFallback: true, // support for html5 mode
noInfo: true, // limit output
proxy: { // proxy all url from /api to ...
'/api': {
target: 'https://other-server.example.com',
}
}
}
...
}
yarn serve v0.18.1
$ webpack-dev-server --open
Project is running at http://localhost:8008/
webpack output is served from /
404s will fallback to /index.html
webpack: wait until bundle finished: /
Hash: 2ad2b42ecd25c7fccb01
Version: webpack 2.2.0-rc.6
Time: 1232ms
Asset Size Chunks Chunk Names
bundle.js 515 kB 0 [emitted] [big] main
chunk {0} bundle.js (main) 500 kB [entry] [rendered]
[34] ./main.js 175 bytes {0} [built]
[35] (webpack)-dev-server/client?http://localhost:8008 4.66 kB {0} [built]
[36] ./~/ansi-regex/index.js 135 bytes {0} [built]
[37] ./userService.js 170 bytes {0} [built]
[39] ./~/events/events.js 8.33 kB {0} [built]
[40] ./~/jquery/dist/jquery.js 267 kB {0} [built]
[42] ./~/punycode/punycode.js 14.7 kB {0} [built]
[45] ./~/querystring-es3/index.js 127 bytes {0} [built]
[48] ./~/sockjs-client/lib/entry.js 244 bytes {0} [built]
[74] ./~/strip-ansi/index.js 161 bytes {0} [built]
[76] ./~/url/url.js 23.3 kB {0} [built]
[77] ./~/url/util.js 314 bytes {0} [built]
[78] (webpack)-dev-server/client/socket.js 856 bytes {0} [built]
[80] (webpack)/hot/emitter.js 77 bytes {0} [built]
[81] multi (webpack)-dev-server/client?http://localhost:8008 ./main.js 40 bytes {0} [built]
+ 67 hidden modules
webpack: bundle is now VALID.
The browser is automatically opened.
Mark that no bundle file is created. All is done in memory.
output: {
filename: 'bundle.js',
path: __dirname + '/bundle', // separate folder
publicPath: '/bundle/'
},
The publicPath
specifies the public URL address of the output files when referenced in a browser.
Specify the bundle folder in your html file
<script src="bundle/bundle.js"></script>
Webpack can bundle any kind of file.
Webpack 'Modules' determine how the different types of modules (files) within a project will be handled.
Babel can help us with:
- ES.Next
- JSX
Install babel-loader (webpack) & babel
yarn add babel-core babel-preset-latest babel-preset-stage-2 --dev
yarn add babel-loader --dev
Configure babel (simplified)
// .babelrc
{
"presets": ["latest", "stage-2"]
}
More information about babel and its configuration see:https://babeljs.io/.
Configure babel-loader in webpack
// webpack.config.js
output: {
...
},
module: {
rules: [
{
test: /\.js$/, // apply babel-loader for any js file
loader: 'babel-loader',
exclude: /node_modules/ // except in node_modules
}
]
}
...
More information about the babel-loader see: https://github.com/babel/babel-loader
Install some more loaders:
npm install style-loader css-loader url-loader --save-dev
Add the css rule in your webpack.config.js
{ test: /\.css$/, loader: 'style-loader!css-loader' }
Add a style sheet
// style.css
body {
background: tomato;
}
And require the file in your main.js
// in your modules just require the stylesheet
// This has the side effect that a <style>-tag is added to the DOM.
require("./style.css");
Re-run webpack and “ta-da”!
Install loader (and dependencies):
npm install less-loader less --save-dev
Add the rule in your webpack.config.js
{ test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }
rename your css to less
mv style.css style.less
require (or import) your scss file
require("./style.less");
and write some less code
@primary-color: LightGray;
body {
background: @primary-color;
}
All what you can't do with a rule
Global extentions on top of the WebPack functionality
Plugin configuration
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [...] // <- add your plugins here
};
Injected variable into our javascript code:
const webpack = require('webpack')
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
},
}),
]
};
In your code you can use the variable:
console.log('Environment: ', process.env);
plugins: [
// uglify JS (obscure & minimize)
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
// minimize other files (css, ...), here for webpack 1.x plugins
new webpack.LoaderOptionsPlugin({
minimize: true
})
]
-
Utility
- webpack.NoErrorsPlugin
- webpack.ProvidePlugin
- html-webpack-plugin
- extract-text-webpack-plugin
- browser-sync-webpack-plugin
- ...
-
Optimize
- webpack.optimize.UglifyJsPlugin
- webpack.optimize.DedupePlugin
- webpack.optimize.CommonsChunkPlugin
- compression-webpack-plugin
- ...
A more production ready config
Choose a developer tool to enhance debugging.
module.exports = {
...
devtool: 'sourcemap'
}
See https://webpack.github.io/docs/configuration.html#devtool
# OSX
NODE_ENV=production webpack
# Windows
set NODE_ENV=production && webpack
# Multi platform
cross-env NODE_ENV=production webpack
// package.json
...
"scripts": {
"build": "cross-env NODE_ENV=production webpack",
}
module.exports = {
output: {
...
}
...
}
if (process.env.NODE_ENV === 'production') {
module.exports.plugins = (module.exports.plugins || []).concat([
// add here your optimize plugins
])
}
By default webpack will bundle all in one module. But for css we typically want a separate style.css file.
npm install --save-dev extract-text-webpack-plugin@^2.0.0-beta # beta !!!
setup config
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
...
module: {
rules: [
// { test: /\.scss$/, loader: 'style-loader!css-loader!less-loader' }
{
test: /\.less$/,
loader: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: "css-loader!less-loader"
})
}
]
},
plugins: [ new ExtractTextPlugin("styles.css") ]
}
Install font
npm install font-awesome --save-dev
Install webpack loaders
# the file-loader emits files
npm install file-loader --save-dev
# the url-loader uses DataUrls
npm install url-loader --save-dev
Config in webpack
module.exports = {
module: {
rules: [
{
test: /\.woff(2)?(\?v=\d+\.\d+\.\d+)?$/,
loader: "url-loader?limit=10000&mimetype=application/font-woff"
},
{
test: /\.(ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: "file-loader"
}
]
}
};
Require (import) the css in your main file
import 'font-awesome/css/font-awesome.css'
Or import the scss in your scss file
$fa-font-path: "~font-awesome/fonts";
@import "~font-awesome/scss/font-awesome";
Add an icon in your html
<i class="fa fa-font-awesome fa-5x"></i>
And your icon is on your page!
Mark that webpack has renamed the font file and modified the css.
"scripts": {
"build": "corss-env NODE_ENV=production webpack",
"serve": "webpack-dev-server --open --hot --inline"
}
Restart webpack-dev-server and thats all. Try to change a css/less file
Hot module replacement doesn't work together with the 'ExtractTextPlugin' plugin.
Tree shaking eliminates unused exports
export function getById(id) {
return { id: 123, name: 'john' }
}
export function getAll() {
return [
{ id: 123, name: 'peter' },
{ id: 222, name: 'robbert' }
]
}
import { getAll } from './userService';
var users = getAll();
users.forEach(function(user) {
console.log(user.id, user.name);
});
Specify babel will not generate CommonJS modules
{
"presets": [
["es2015", { "modules": false }]
]
}
And run webpack with the optimise flag
$ webpack --optimize-minimize
Books
https://github.com/survivejs/webpack
Articles
https://medium.com/@rajaraodv/webpack-the-confusing-parts-58712f8fcad9
https://blog.madewithlove.be/post/webpack-your-bags/
Tools
https://github.com/survivejs/webpack-merge