This repo is a collection of simple demos of Webpack.
These demos are purposely written in a simple and clear style. You will find no difficulty in following them to learn the powerful tool.
How to use
First, install Webpack and webpack-dev-server globally.
$ npm i -g webpack webpack-dev-server
Copy the code
Then, clone the repo and install the dependencies.
$ git clone [email protected]:ruanyf/webpack-demos.git
$ cd webpack-demos
$ npm install
Copy the code
Now, play with the source files under the repo’s demo* directories.
$ cd demo01
$ webpack-dev-server
Copy the code
Visit http://127.0.0.1:8080 with your browser.
Foreword: What is Webpack
Webpack is a front-end build systems like Grunt and Gulp.
It can be used as a module bundler similar to Browserify, and do much more.
$ browserify main.js > bundle.js
# be equivalent to
$ webpack main.js bundle.js
Copy the code
Its configuration file is webpack.config.js
.
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
Copy the code
After having webpack.config.js
, you can invoke Webpack without any arguments.
$ webpack
Copy the code
Some command-line options you should know.
webpack
— For building once for developmentwebpack -p
— For building once for production (Minification)webpack --watch
– For Continuous Incremental buildwebpack -d
– To include source mapswebpack --colors
— For making things pretty
To produce a production ready application, you could write scripts
field in your package.json file as following.
// package.json
{
// ...
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors",
"deploy": "NODE_ENV=production webpack -p"
},
// ...
}
Copy the code
Demo01: Entry file (source)
Entry file is a file which Webpack will read to build bundle.js.
For example, main.js
is an entry file.
// main.js
document.write('<h1>Hello World</h1>');
Copy the code
index.html
<html>
<body>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
Copy the code
Webpack follows webpack.config.js
to build bundle.js
.
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
Copy the code
Launch the server, visit http://127.0.0.1:8080.
$ webpack-dev-server
Copy the code
Demo02: Multiple entry files (source)
Multiple entry files are allowed. It is useful for a multi-page app.
// main1.js
document.write('<h1>Hello World</h1>');
// main2.js
document.write('<h2>Hello Webpack</h2>');
Copy the code
index.html
<html>
<body>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>
Copy the code
webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js'
},
output: {
filename: '[name].js'
}
};
Copy the code
Demo03: Babel-loader (source)
Loaders are preprocessors which transform a resource file of your app (more info). For example, Babel-loader can transform JSX/ES6 file into JS file. Official doc has a complete list of loaders.
main.jsx
is a JSX file.
const React = require('react'); const ReactDOM = require('react-dom'); ReactDOM.render( <h1>Hello, world! </h1>, document.querySelector('#wrapper') );Copy the code
index.html
<html>
<body>
<div id="wrapper"></div>
<script src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
},
]
}
};
Copy the code
In webpack.config.js
, module.loaders
field is used to assign loaders. The above snippet uses babel-loader
which also needs plugins babel-preset-es2015 and babel-preset-react to transpile ES6 and React. You can also take another way to set the babel query option.
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'react']
}
}
]
}
Copy the code
Demo04: CSS-loader (source)
Webpack allows you to require CSS in JS file, then preprocessed CSS file with CSS-loader.
main.js
require('./app.css');
Copy the code
app.css
body {
background-color: blue;
}
Copy the code
index.html
<html>
<head>
<script type="text/javascript" src="bundle.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
Copy the code
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{ test: /\.css$/, loader: 'style-loader!css-loader' },
]
}
};
Copy the code
Attention, you have to use two loaders to transform CSS file. First is CSS-loader to read CSS file, and another is Style-loader to insert Style tag into HTML page. Different loaders are linked by exclamation mark(!) .
After launching the server, index.html
will have inline style.
<head>
<script type="text/javascript" src="bundle.js"></script>
<style type="text/css">
body {
background-color: blue;
}
</style>
</head>
Copy the code
Demo05: Image loader (source)
Webpack could also require images in JS files.
main.js
var img1 = document.createElement("img");
img1.src = require("./small.png");
document.body.appendChild(img1);
var img2 = document.createElement("img");
img2.src = require("./big.png");
document.body.appendChild(img2);
Copy the code
index.html
<html>
<body>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }
]
}
};
Copy the code
url-loader transforms image files. If the image size is smaller than 8192 bytes, it will be transformed into Data URL; otherwise, it will be transformed into normal URL. As you see, question mark(?) is be used to pass parameters into loaders.
After launching the server, small.png
and big.png
will have the following URLs.
<img src="data:image/png; base64,iVBOR... uQmCC"> <img src="4853ca667a2b8b8844eb2693ac1b2578.png">Copy the code
Demo06: CSS Module (source)
css-loader? modules (the query parameter modules) enables the CSS Modules spec.
It means your module’s CSS is local scoped CSS by default. You can switch it off with :global(...)
for selectors and/or rules. (more info)
index.html
<html>
<body>
<h1 class="h1">Hello World</h1>
<h2 class="h2">Hello Webpack</h2>
<div id="example"></div>
<script src="./bundle.js"></script>
</body>
</html>
Copy the code
app.css
.h1 {
color:red;
}
:global(.h2) {
color: blue;
}
Copy the code
main.jsx
var React = require('react');
var ReactDOM = require('react-dom');
var style = require('./app.css');
ReactDOM.render(
<div>
<h1 className={style.h1}>Hello World</h1>
<h2 className="h2">Hello Webpack</h2>
</div>,
document.getElementById('example')
);
Copy the code
webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules'
}
]
}
};
Copy the code
Launch the server.
$ webpack-dev-server
Copy the code
Visit http://127.0.0.1:8080, you’ll find that only second h1 is red, because its CSS is local scoped, and both h2 is blue, because its CSS is global scoped.
Demo07: UglifyJs Plugin (source)
Webpack has a plugin system to expand its functions. For example, UglifyJs Plugin will minify output(bundle.js
) JS codes.
main.js
var longVariableName = 'Hello';
longVariableName += ' World';
document.write('<h1>' + longVariableName + '</h1>');
Copy the code
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
var webpack = require('webpack');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new uglifyJsPlugin({
compress: {
warnings: false
}
})
]
};
Copy the code
After launching the server, main.js
will be minified into following.
var o="Hello"; o+=" World",document.write("<h1>"+o+"</h1>")Copy the code
Demo08: HTML Webpack Plugin and Open Browser Webpack Plugin (source)
This demo shows you how to load 3rd-party plugins.
html-webpack-plugin could create index.html
for you, and open-browser-webpack-plugin could open a new browser tab when Webpack loads.
main.js
document.write('<h1>Hello World</h1>');
Copy the code
webpack.config.js
var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new HtmlwebpackPlugin({
title: 'Webpack-demos'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8080'
})
]
};
Copy the code
Run webpack-dev-server
.
$ webpack-dev-server
Copy the code
Now you don’t need to write index.html
by hand and don’t have to open browser by yourself. Webpack did all these things for you.
Demo09: Environment flags (source)
You can enable some codes only in development environment with environment flags.
main.js
document.write('<h1>Hello World</h1>');
if (__DEV__) {
document.write(new Date());
}
Copy the code
index.html
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};
Copy the code
Now pass environment variable into webpack.
# Linux & Mac
$ env DEBUG=true webpack-dev-server
# Windows
$ DEBUG=true webpack-dev-server
Copy the code
Demo10: Code splitting (source)
For big web apps it’s not efficient to put all code into a single file, Webpack allows you to split them into several chunks. Especially if some blocks of code are only required under some circumstances, these chunks could be loaded on demand.
At first, you use require.ensure
to define a split point. (official document)
// main.js
require.ensure(['./a'], function(require) {
var content = require('./a');
document.open();
document.write('<h1>' + content + '</h1>');
document.close();
});
Copy the code
require.ensure
tells Webpack that ./a.js
should be separated from bundle.js
and built into a single chunk file.
// a.js
module.exports = 'Hello World';
Copy the code
Now Webpack takes care of the dependencies, output files and runtime stuff. You don’t have to put any redundancy into your index.html
and webpack.config.js
.
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
};
Copy the code
Launch the server.
$ webpack-dev-server
Copy the code
On the surface, you won’t feel any differences. However, Webpack actually builds main.js
and a.js
into different chunks(bundle.js
and 1.bundle.js
), and loads 1.bundle.js
from bundle.js
when on demand.
Demo11: Code splitting with bundle-loader (source)
Another way of code splitting is using bundle-loader.
// main.js // Now a.js is requested, it will be bundled into another file var load = require('bundle-loader! ./a.js'); // To wait until a.js is available (and get the exports) // you need to async wait for it. load(function(file) { document.open(); document.write('<h1>' + file + '</h1>'); document.close(); });Copy the code
require(‘bundle-loader! ./a.js’) tells Webpack to load a.js from another chunk.
Now Webpack will build main.js
into bundle.js
, and a.js
into 1.bundle.js
.
Demo12: Common chunk (source)
When multi scripts have common chunks, you can extract the common part into a separate file with CommonsChunkPlugin.
// main1.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('a')
);
// main2.jsx
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h2>Hello Webpack</h2>,
document.getElementById('b')
);
Copy the code
index.html
<html>
<body>
<div id="a"></div>
<div id="b"></div>
<script src="init.js"></script>
<script src="bundle1.js"></script>
<script src="bundle2.js"></script>
</body>
</html>
Copy the code
webpack.config.js
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
bundle1: './main1.jsx',
bundle2: './main2.jsx'
},
output: {
filename: '[name].js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
]
},
plugins: [
new CommonsChunkPlugin('init.js')
]
}
Copy the code
Demo13: Vendor chunk (source)
You can also extract the vendor libraries from a script into a separate file with CommonsChunkPlugin.
main.js
var $ = require('jquery');
$('h1').text('Hello World');
Copy the code
index.html
<html>
<body>
<h1></h1>
<script src="vendor.js"></script>
<script src="bundle.js"></script>
</body>
</html>
Copy the code
webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
app: './main.js',
vendor: ['jquery'],
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */'vendor', /* filename= */'vendor.js')
]
};
Copy the code
If you want a module available as variable in every module, such as making $ and jQuery available in every module without writing require("jquery")
. You should use ProvidePlugin
(Official doc).
// main.js
$('h1').text('Hello World');
// webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: {
app: './main.js'
},
output: {
filename: 'bundle.js'
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
};
Copy the code
Demo14: Exposing global variables (source)
If you want to use some global variables, and don’t want to include them in the Webpack bundle, you can enable externals
field in webpack.config.js
(official document).
For example, we have a data.js
.
var data = 'Hello World';
Copy the code
We can expose data
as a global variable.
// webpack.config.js
module.exports = {
entry: './main.jsx',
output: {
filename: 'bundle.js'
},
module: {
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
]
},
externals: {
// require('data') is external and available
// on the global var data
'data': 'data'
}
};
Copy the code
Now, you require data
as a module variable in your script. but it actually is a global variable.
// main.jsx
var data = require('data');
var React = require('react');
var ReactDOM = require('react-dom');
ReactDOM.render(
<h1>{data}</h1>,
document.body
);
Copy the code
Demo15: Hot Module Replacement (source)
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.
You have two ways to enable Hot Module Replacement with the webpack-dev-server.
(1) Specify --hot
and --inline
on the command line
$ webpack-dev-server --hot --inline
Copy the code
Meaning of the options:
--hot
: adds the HotModuleReplacementPlugin and switch the server to hot mode.--inline
: embed the webpack-dev-server runtime into the bundle.--hot --inline
: also adds the webpack/hot/dev-server entry.
(2) Modify webpack.config.js
.
- add
new webpack.HotModuleReplacementPlugin()
to theplugins
field - add
webpack/hot/dev-server
andwebpack-dev-server/client? http://localhost:8080
to theentry
field
webpack.config.js
looks like the following.
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8080',
'./index.js'
],
output: {
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['babel-loader'],
query: {
presets: ['es2015', 'react']
},
include: path.join(__dirname, '.')
}]
}
};
Copy the code
Now launch the dev server.
$ webpack-dev-server
Copy the code
Visiting http://localhost:8080, you should see ‘Hello World’ in your browser.
Don’t close the server. Open a new terminal to edit App.js
, and modify ‘Hello World’ into ‘Hello Webpack’. Save it, and see what happened in the browser.
App.js
import React, { Component } from 'react'; export default class App extends Component { render() { return ( <h1>Hello World</h1> ); }}Copy the code
index.js
import React from 'react';
import ReactDOM = require('react-dom');
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Copy the code
index.html
<html>
<body>
<div id='root'></div>
<script src="/static/bundle.js"></script>
</body>
</html>
Copy the code
Demo16: React router (source)
This demo uses webpack to build React-router’s official example.
Let’s imagine a little app with a dashboard, inbox, and calendar.
+---------------------------------------------------------+ | +---------+ +-------+ +--------+ | | |Dashboard| | Inbox | |Calendar| Logged in as Jane | | +---------+ +-------+ +--------+ | +---------------------------------------------------------+ | | | Dashboard | | | | | | +---------------------+ +----------------------+ | | | | | | | | | + + | +---------> | | | | | | | | | | | | | + | | +-------------> | | | | | | + | | | | | | | | | | | | | | | | +-+---+----+-----+----+ +----------------------+ | | | +---------------------------------------------------------+Copy the code
$ webpack-dev-server --history-api-fallback
Copy the code
Useful links
- Webpack docs
- webpack-howto, by Pete Hunt
- Diving into Webpack, by Web Design Weekly
- Webpack and React is awesome, by Christian Alfoni
- Browserify vs Webpack, by Cory House
- React Webpack cookbook, by Christian Alfoni
License
MIT