Basics Of Webpack Module Bundler - Config, Dev Server, Loaders
Reading Time:
Reading Time:
Larger the application, more modules are created, leading to more http requests from client. With webpack we can bundle them into single file. This can be done not only to JavaScript but also to CSS and others assets.
Watch this presentation by pete hunt on How Instagram.com Works using webpack.
Using webpack we can,
webpack uses the module system provided by Node.js to find dependencies between each modules and bundles them into a single file based on those dependencies. If a file exists without any file depending on it, then it must be specified separately to be bundled.
To install webpack, use the node package manager.
npm install webpack -g
The -g
is to install globally rather than for the project, but it is advised to install it per project as a devDependency.
Using the webpack command line commands, we can get started with webpack quickly with the basic functionalities up and running. Let’s create a few module files representing a web application and see what webpack can do.
Modules – login-module.js
, signup-module.js
and app.js
The directory structure,
console.log('Login module is ready.');
console.log('Signup module is ready, and it will build after i save.');
/**
* This is the entry point for webpack
*/
require('./login-module.js');
require('./signup-module.js');
console.log('App has been loaded.');
First to determine our entry script, a script that can be processed and our entire modules dependencies are included. app.js
requires/includes both the login-module.js
and signup-module.js
files. So our entry point for building the source would be app.js
. We could also have multiple entry points, will look into that later.
Then to determine the location from which the output file(bundled file) with all the modules combined will be served from. We’ll create a dist folder(short for distribution) and we’ll name our output file bundle.js
, makes sense right, we are bundling all the modules. You can name it whatever you want it to be, but specify it correctly while linking it to the HTML page to be included in.
webpack ./app.js ./dist/bundle.js —progress —colors —watch
In the above command,
./app.js
– The first argument informs webpack what file is going to be the entry point for building the app../dist/bundle.js
– The second argument tell’s it where it needs to store the bundled output file.Apart from the arguments we passed, there are few other options too that we can pass to webpack to modify it’s behaviour.
—progress
– Will tell webpack to show the build progress status.—colors
– You know what this means.—watch
– Now this here is really useful stuff, this options tells webpack to keep watching the source module files for changes. When there is a change made, it recompiles and generate the output bundle file. You still need to reload the browser to relink the updated bundle file, but we’ll get to automating that later.Now if you look at the output bundle file, there will be some extra code, that you did not write. It’s what webpack uses to map the modules and load the code that is needed.
So far we have just used the webpack command line, but it’s not good enough to specify the options and configs in the command line every time you need to build using webpack.
Let’s just move all the arguments and options needed for webpack in a separate file, so it’s easier to maintain and there are lot’s more options available in webpack when using a config file.
Note: Webpack is very strict about the configurations, if the configs are not right, it just won’t work.
The webpack config file is basically a JavaScript file with all options as a Object key values specifying the options for webpack and added to the Node module exports. For now let’s add the command line options in the config file.
The config file looks as below,
module.exports = {
// The entry point for the application
entry: ["./app.js"],
output: {
// Where to put the output source.
path: "./dist/",
// Name of the output source file.
filename: "bundle.js"
},
// Tells webpack to keep watching for changes and recompile if changes are found.
watch: true,
};
The entry script is given in the entry
key, the value can be an array, since webpack can have multiple entry scripts. This is identical to the first argument in the command line.
The output
key is an object that specifies two options for now. filename
which is the output file name(eg. bundle.js) and path
is where the output file is to be stored. This is identical to the second argument in the command line.
Finally the watch
option informs webpack to keep watching the files for changes and is identical to the command line —watch
option.
Now just run the webpack
in the command line and you will see the output file in the dist directory.
webpack
With the config file, there is a lot’s of options we can use to configure webpack to use it’s full capabilities.
webpack-dev-server is another one of the cool NPM package for webpack which is used to speed up the development process by automating the build and run process. It watches for changes in files just like the watch option we used, but webpack-dev-server refreshes the browser web page automatically, making it easier to view the changes into effect immediately instead of us refreshing the browser manually.
Note: webpack-dev-server is nothing but a node.js Express server, which uses webpack-dev-middleware and socket.io to serve and reload in realtime.
To use the webpack-dev-server, we need to install the package first.
npm install webpack-dev-server -g
Again we specify the -g flag to install it globally.
Once it is installed, in the root directory of the project, i.e where our webpack config file is, just run webpack-dev-sever
command. You can also place the webpack config file in a separate directory and specify the config file using the —config [path/to/webpack.config.js]
option. Here the config file is stored inside of the config directory.
To configure the webpack-dev-server to serve the files, you need to specify the base folder relative path from which webpack will serve the build using the publicPath
key of the output
object in the webpack config.
module.exports = {
// The entry point for the application
entry: ["./app.js"],
output: {
// Where to put the output source.
path: "./dist/",
// Name of the output source file.
filename: "bundle.js",
// Required for webpack-dev-server, provide the path from which
// the file will be served to front end.
publicPath: '/dist/'
}
};
As you can see in the output above, webpack instead of the process being ended, it is watching for changes in files to recompile. It now shows bundle is now VALID
The webpack-dev-server does not actually write the output to the bundle.js file, instead it serves the file from memory. So you won’t actually see the output in the bundle.js. Since webpack-dev-server serves the files from memory, you need to specify this in the HTML file which needs the output bundle.
For example, if the file is served from dist directory
http://localhost:8080/dist/bundle.js
<html>
<head>
<title>Webpack Basics - Command Line / Config file</title>
</head>
<body>
<h3>Welcome to webpack</h3>
<p><b>Open the console to view the modules messages.</b></p>
<script type="text/javascript" src="dist/bundle.js"></script>
</body>
</html>
webpack-dev-server has two modes of behaviour,
Once you have configures webpack and started the webpack dev server, it builds the source files is ready to server. Open the browser and visit,
http://localhost:8080/webpack-dev-server/index.html
When the page is loaded, you see a App status bar at the top of the browser. Basically your app is embedded in an iframe and is auto refreshed when changes are made.
Once the command is run, webpack builds and bundles the source files and serves the output file.
In the browser there is a App status bar shown which specifies the status of the build and refreshes the browser every time a change is made. If you don’t want to get that in your way of development, you can use the —inline
option in webpack. Once specified, the dev-server will do a auto refresh of the application and you won’t see the App status bar this time, but you get the autorefresh feature out of the box.
webpack-dev-server —config config/webpack.config.js —inline
Once the —inline
is specified you don’t see the status bar anymore and you are good to go.
read more on webpack-dev-server.
Loaders are one of the important features of webpack. Apart from just combining modules and producing an output, with loaders you can process and transform js source code. They are just functions that takes in as input the source file and return the processed/transformed new source.
For example you can convert JSX to JavaScript, CoffeeScript to JS, Process LESS and SASS into CSS etc using loaders. You can find the list of loaders here
Here are some of the loaders features described in the official webpack documentation,
Loaders have a general naming convention of XXX-loader, where XXX can be the loader name. For example babel-loader. It can be referenced either with just the loader name(babel) or using the full name(babel-loader).
Loaders are just another npm package. They can be used by configuring in the webpack config file. There are two ways loaders can be used, as a
First install the loader package that we are going to be using by using the npm
command.
npm install jshint jshint-loader —save-dev
Here the jshint and its loader jshint-loader is installed.
To configure webpack to run this preLoader(there can be many preLoaders) modify the webpack config file as shown below.
....
module: {
preLoaders: [
{
test: /.js$/, // will process all the js files
exclude: /node_modules/,
loader: "jshint-loader"
}
]
},
....
Add a preLoaders
array of loader objects in the module
object of the webpack config. Each loader object has some configs options that tells which files the loader should process and which loader to parse it with.
test
– Regular Expression to match the file’s that should to be processed with this loader.exclude
– Regular Expression to exclude the folders and files that should not be processed with the loader.loader
– The loader which must be used must be specified in this options. e.g. jshint-loader
In addition to the above configurations, you can also pass a query
object, which contains options specific to the loader, that will be sent as a config to the loader to modify it’s behaviour.
Once the config has been added, run the webpack
command. Once you run it, you will see the following output.
In the output above, jshint is reporting that there is a semicolon missing in the login-module.js and in the signup-module.js the document.write
can be a form of eval and that there is a semicolon missing there too. We can go ahead and fix those.
Once fixed, those warning do not appear.
Loaders and preLoaders are the same. Only difference is that preLoaders run before the loaders are executed. Now let’s install babel-loader
which can process and transform React JSX and ES6 code to JavaScript.
npm install react react-dom babel-preset-react babel-loader babel-core babel-preset-es2015 —save-dev
Note: In the above installation babel-preset-es2015
and babel-preset-react
are loader specific packages to compile and process es6 and JSX.
Let’s include a sample react application from the react website with JSX syntax. Let’s name it timer.jsx
and put it in the root folder.
var React = require('react');
var ReactDOM = require('react-dom');
// Sampel Timer app from - https://facebook.github.io/react/index.html
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
);
}
});
ReactDOM.render(, document.getElementById('timer'));
....
module: {
preLoaders: [
{
test: /.js$/, // will process all the js files
exclude: /node_modules/,
loader: "jshint-loader"
}
],
loaders: [
// https://github.com/babel/babel-loader
{
test: /.jsx$/, // Will process all the jsx files
exclude: /node_modules/,
loader: "babel",
query: {
presets: ['react']
}
}
]
},
...
Run the webpack command now.
The above output shows that React JSX has been successfully compiled and the output rendered properly.
Note: You can also compile and process es6 code into javascript by using the preset as es2015 in the query object of bable-loader.
Source map is built into webpack. It allows us to view the source of the file that we have written directly instead of the code webpack has bundled all together. This helps us identify the code for debugging purpose and make changes easily.
To enable source map, use the -d
flag while running webpack
or webpack-dev-server
.
Let’s add a debugger
statement inside the render
method and see what happens without sourmap enabled.
webpack-dev-server --config config/webpack.config.js --inline -d
Once the source map is enabled, the debugger will point to the exact source script where the debugger statement is
Once you understand the basic of webpack, you can do lot more things using plugin and other set of amazing features webpack offers.
To learn more head to the webpack documentation.