Front-end development has shifted to a modular approach, improving the encapsulation and structure of codebases. Tooling became a critical part of any project, and right now there are a lot of possible choices.
Webpack has gained popularity in the last years because of its power and scalability, but some developers found its configuration process confusing and hard to adopt.
We’ll go step by step from an empty configuration file to a simple but complete setup to bundle a project. This article assumes basic understanding of CommonJS notation and how modules work.
Concepts
Unlike most bundlers out there, the motivation behind Webpack is to gather all your dependencies (not just code, but other assets as well) and generate a dependency graph.
At first, it might look strange to see a `.js` file require a stylesheet, or a stylesheet retrieving an image modified as it was a module, but these allow Webpack to understand what is included in your bundle and helps you transform and optimize them.
Install
Let’s first add the initial packages we are going to use:
npm install webpack webpack-dev-server –save-dev
Next we create a `webpack.config.js` file in the root of our project and add two scripts to our `package.json` files for both local development and production release.
scripts: {
start: webpack-dev-server,
build: webpack
}
Webpack commands will pick up the config file we’ve just created unless we indicate other action.
Entry
There are many ways to specify our “entry point”, which will be the root of our dependencies graph.
The easiest one is to pass a string:
var baseConfig = {
entry: ./src/index.js
};
We could also pass an object in case we need more than one entry in the future.
var baseConfig = {
entry: {
main: ./src/index.js
}
};
I recommend the last one since it will scale better as your project grows.
Output
The output in Webpack is an object holding the path where our bundles and assets will go, as well as the name the entries will adopt.
var path = require(path);
var baseConfig = {
entry: {
main: ./src/index.js
},
output: {
filename: main.js,
path: path.resolve(./build)
}
};
// export configuration
module.exports = baseConfig;
If you’re defining the entry with an object, rather than hardcoding the output filename with a string, you can do:
output: {
filename: [name].js,
path: path.resolve(./build)
}
This way when new entries are added Webpack will pick up their key to form the file name.
With just this small set of configurations, we are already able to run a server and develop locally with npm start or npm run build to bundle our code for release. By knowing the dependencies of the project, webpack-dev-server will watch them and reload the site when it detects one of them has changed.
Loaders
The goal of Webpack is to handle all our dependencies.
// index.js file
import helpers from /helpers/main.js;
// Hey Webpack! I will need these styles:
import main.css;
What’s that? Requiring a stylesheet in JavaScript? Yes! But bundlers are only prepared to handle JavaScript dependencies out-of-the-box. This is where “loaders” make their entrance.
Loaders provide an easy way to intercept our dependencies and preprocess them before they get bundled.
var baseConfig = {
// …
module: {
rules: [
{
test: /* RegEx */,
use: [
{
loader: /* loader name */,
query: /* optional config object */
}
]
}
]
}
};
For loaders to work, we need a regular expression to identify the files we want to modify and a string or an array with the loaders we want to use.
Styles
To allow Webpack to process our styles when required we are going to install css and style loaders.
npm install –save-dev css-loader style-loader
The css-loader will interpret styles as dependencies and the style-loader will automatically include a