WittCode💻

Webpack Production and Development

By

Learn about webpack production vs development, configuring webpack with separate webpack production and development files, the difference between a development and production environment, and why different environments are needed.

Table of Contents 📖

Production vs Development

When developing an application it is essential to have an environment for production and one, or even more, for development. There are many reasons for this, but typically we want our application out in production to be as fast and small as possible while we want our application in development to provide useful debugging information such as logging, source maps, etc. In other words, we want development to be a good experience for the developer and production to be a good experience for the end user.

Webpack and Environments

Webpack can be configured to process code in 3 different ways: production, development, or none.

  • production - sets process.env.NODE_ENV to production and enables several different plugins such as TerserPlugin to minimize code, ModuleConcatenationPlugin to concatenate modules, etc.
  • development - sets process.env.NODE_ENV to development and enables meaningful names for modules/chunks for better debugging
  • none - no default optimization options are used

By default, webpack processes code for production. As a demonstration, create a simple webpack configuration file with an entry point, output, and the HTML webpack plugin to place the outputted JavaScript bundle into an HTML file.

const htmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
        clean: true
    },
    plugins: [
        new htmlWebpackPlugin(),
    ]
}

Inside this index.js file, place a conditional statement that logs to the console based on the current Node environment. Lets also write some code that will introduce an error by calling a function that does not exist.

process.env.NODE_ENV === 'development' ?
    console.log('I am in development mode!') : console.log('I am in production mode!');

doesNotExist();

Next, run webpack with webpack --config webpack.config.js.

webpack --config webpack.config.js

If we look at the outputted index.html file, we can see that it is minified thanks to the TerserPlugin that webpack enables.

<!doctype html><html><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"><script defer="defer" src="bundle.js"></ script></head><body></body></html>

Also, when serving up this index.html file, if we check the console we can see that I am in production mode is printed as webpack set NODE_ENV to production. We can also see that the error that is printed is not very helpful as it points out the location in the bundled file.

Image

We can tell webpack to process code as ready for development or production by using the mode key in a webpack configuration file. Lets set the mode to development.

const htmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
        clean: true
    },
    plugins: [
        new htmlWebpackPlugin(),
    ]
}

Now lets run webpack.

webpack --config webpack.config.js

Then take a look at the output HTML file.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Webpack App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script defer src="bundle.js"></ script>
        </head>
    <body>
    </body>
</html>

The code is no longer minified as the TerserPlugin is not activated. We can also see some more useful error messages in the console.

Image

Here it gives the exact line number where the non-existent method is.

Multiple Webpack Configuration Files

However, toggling between development and production mode isn't very efficient. The best way to handle this is to create two different webpack configuration files. One for production and one for development. Create two webpack configuration files called webpack.dev.js for development and webpack.prod.js for production.

[object Object],[object Object]

Now, before we add anything to these files, lets create some npm scripts to run our application in development mode and production mode.

"scripts": {
    "start": "webpack --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
},

Now, running npm run build will create a production ready build of our application and running npm start will run our application in development mode.

Webpack Development Configuration File

Lets start with the development configuration file. To begin, lets set the mode to development, provide an entry point, an output, and provide the HTML webpack plugin.

const htmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
        clean: true
    },
    plugins: [
        new htmlWebpackPlugin(),
    ]
}

We should also make it so that we don't have to run npm start each time we make changes. This manual process is very inefficient and time consuming. Instead, we can use webpack-dev-server which will recompile our code for us and also refresh the browser when it detects saved changes. Install it from npm as a development dependency with npm i webpack-dev-server -D.

npm i webpack-dev-server -D

Now lets tell the webpack server to serve up the files in the build folder on port 9999. The webpack development server is configured with the devServer key.

const htmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build'),
        clean: true
    },
    devServer: {
        static: './build',
        port: 9999,
        open: true
    },
    plugins: [
        new htmlWebpackPlugin(),
    ]
}

We also add the open key and set it to true so that the webpack development server opens the default browser after it has been started. Now lets alter our start script to use the webpack dev server. All we have to do is add serve to the script.

"start": "webpack serve --config webpack.dev.js",

When we run the application with npm start notice how the build folder isn't created. This is because webpack-dev-server keeps the bundled files in memory as opposed to writing them to disk.

Webpack Production Configuration File

Now lets work with the production configuration file. To begin, lets set the mode to production, provide an entry point, an output, and provide the HTML webpack plugin. We also want to use substitution in the output filename so our changes are not cached.

const htmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, 'build'),
        clean: true
    },
    plugins: [
        new htmlWebpackPlugin(),
    ]
}

This is all we will be adding to our production file as we do not need a development server for production. However, here we are only using JavaScript. If other file types are used other than JavaScript, such as CSS, they should be minimized too through their appropriate plugins and loaders.