Learn Webpack - Sample Chapter
Learn Webpack - Sample Chapter
By Jakob Lind
https://createapp.dev/webpack-book/
In this sample chapter, you’ll first learn how to create two webpack config files - one for dev and
one for prod. Then you will continue to learn how to create two different bundles - one for admin
and one for the public facing site.
To be able to follow this you must have an existing project to start from. You can download a
minimalistic webpack project on https://createapp.dev/
Enjoy!
Separate prod and dev configs
A simple webpack config for a React app looks something like this as you already
learned:
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [
'.js',
'.jsx'
]
}
}
module.exports = config;
1
"build-dev": "webpack -d --mode development",
"build-prod": "webpack -p --mode production"
},
If this doesn’t look familiar, take a look at Part 1 of the book again.
Now, the problem with this is that there is only one webpack config for both production
and development. That means if you add some extra optimizations meant for production
in the webpack config, you will also get those optimizations in your development build.
What you want to do is to have separate webpack configs for development and
production.
If we look at the docs for webpack-cli (https://webpack.js.org/api/cli/) you can see that it
accepts an input parameter called --config. Here you can specify which config file to
use when you build. You can use this parameter in the package.json file where you
specify how to call the webpack cli:
"scripts": {
"build-dev": "webpack -d --mode development --config
webpack.dev.config.js",
"build-prod": "webpack -p --mode production --config
webpack.prod.config.js"
},
Note that you now use two separate webpack configs: webpack.dev.config.js and
webpack.prod.config.js. Now you just need to create these files. Let’s just do that by
copying the old webpack.config.js:
cp webpack.config.js webpack.prod.config.js
mv webpack.config.js webpack.dev.config.js
Now you have separate configs for prod and dev. You can add hot reloading to the dev
config and optimizations to the prod config.
2
But there is one problem.
Most of the dev config and the prod config will contain the same stuff. Things like the
entry and the loaders will be the same in prod and dev.
There is an awesome library that is called webpack-merge that helps us merge two
webpack configs. We will use this library to merge the base config with the prod config
and the dev config.
And then we will create the webpack.base.config.js which look exactly like our old
webpack.config.js:
const config = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
3
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [
'.js',
'.jsx'
]
}
}
module.exports = config;
Now let’s extend from the base config in the webpack.dev.config.js file
const config = {
devServer: {
contentBase: './dist'
}
}
This dev config will add the devServer section to the base config. You will learn more
about devServer in the next chapter where you’ll create an awesome dev experience.
4
Note how we use the function merge from the library webpack-merge to merge the base
config that we just created and the new dev config. The merge function creates a config
that looks like this:
{
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [
'.js',
'.jsx'
]
},
devServer: {
contentBase: './dist'
}
}
5
const config = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
This prod config uses the optimization config item that will extract the dependencies
to its own bundle to improve caching. You will learn more about this in the caching
chapter
Again we use merge function to merge the webpack.base.config.js with the prod
webpack config. The merge function creates a config that will look like this:
{
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [
'.js',
'.jsx'
6
]
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
That’s it. Now we have DRYed up the code. You can build the prod bundle and dev
bundle just like before:
There are more use cases where it is useful to create two configs. That is when you
want two different outputs - that means creating two different bundles.
One use case for this is if your app has two distinctly different parts. It could be that one
part is the public part and the other is the admin. You don’t want the code for the admin
part to be visible in the public bundle because that would be a security vulnerability.
Another example is if you want to implement SSR (server-side rendering). Then you
want to run the client side code and the backend code through webpack. That means
you want two outputs.
7
How to make two bundles - public and admin
Let’s start looking at the case where you have two distinct parts of your website. The
authorized user might see the admin part of the app and everyone else can see only the
public facing site.
We start by creating the webpack.base.config.js file that both our webpack configs
will merge with:
const config = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [
'.js',
'.jsx'
]
}
}
module.exports = config;
Note that we don’t specify the entry or the output because we will do that in the other
config files. Now let’s extend from the base config in the webpack.public.config.js
file:
8
const merge = require('webpack-merge');
const base = require('./webpack.base.config.js');
const config = {
entry: './src/index-public.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
}
const config = {
entry: './src/index-admin.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'admin-bundle.js'
},
}
To build this you need to run webpack twice: once for the public bundle and once for the
admin bundle. Let’s create a script in package.json that does this for us:
"scripts": {
"build-public": "webpack -d --mode production --config
webpack.public.config.js",
9
build-admin": "webpack -p --mode production --config
"
webpack.admin.config.js",
"build": "npm run build-public; npm run-build-admin"
},
If you now run npm run build it will run both build-public and build-admin which
will create two bundles for you.
10