I efficiently learned "webpack development environment configuration" using this idea
Tags: javascript, beginners, tutorial, webdev
Published: 2023-09-08 08:33:53

## Preface
We often see structures like the following when using `webpack`:
```tree
build
├── webpack.common.js
└── webpack.dev.js
└── webpack.prod.js
```
Often times, `webpack` configurations we basically copy and paste without thinking about why we're breaking the config file down into so many. As a result, when we learn to configure `webpack` on our own, we often have problems such as the following:
- Slow builds in development environments
- Blank pages after packaging
- Resources not found

In fact, these problems, all stem from us:
- Not understanding `webpack` to build the development environment, production environment, the two environment configuration of the difference in thinking
- We don't have a clear understanding of the file directories of the two environments.

So this article is the same as before, will not just copy the configuration over, but hope that **summarize the relevant methods**, so that we can better learn to configure.

## Learning Outline
This article focuses on:
- The core idea of `webpack` development environment configuration.
- Completing a development environment configuration based on the core concepts
- Explain some confusing points in the `devServer` configuration.
- Summarize the core ideas of `webpack` development environment configuration.

## Remarks
The article has been uploaded to [github](https://github.com/limingcan562/learn-webpack-5):

- For the sake of reading convenience, the article only posted the relevant code, it is recommended to `fork` it to see the complete code; or follow along with the article and write the code yourself
- It's not easy to create, if you think it's helpful, welcome to `star`🌟!

## Development Environment Core Ideas
We mentioned above that we often see directories like the following when using `webpack`:
```tree
build
├── webpack.common.js
└── webpack.dev.js
└── webpack.prod.js
```

Why do we need to break the configuration file into so many, so cumbersome?

Let's first review, in our actual development, in order to project can be stable and safe on-line, we will generally be divided into several environments:
- Development environment: local development, debugging environment
- Test environment: we complete the local development, the code will be deployed to our test server, for testing
- Production environment: the test passed, the code will be deployed to the official server, the official on-line

> Some of the more rigorous tests will have pre-production environments, etc.

Our configuration should be specific to the environment, so of course our configuration should not be the same for different environments.

### Purpose
We need to be clear about what the fundamental purpose of the development environment is.

**The fundamental purpose of the development environment is to quickly locate problems during the development process, to see the effect of debugging faster, and to improve development efficiency.**

Especially as a front-end, inseparable from the visual dealings. We can't wait to `Ctrl+S`, immediately after the effect, so that we can quickly debug. Imagine if every time we write an effect, we have to wait for a few seconds before we can see it, will this drive us crazy?

### Ideas
Therefore, in order for us to see the effect debugging as soon as possible, reducing the `webpack` compilation process, **development environment, the configuration should be everything from simple**:
- For styles, we don't need to separate them and insert them directly into the `<head />` with the `style-loader`.
- For `js`, `img`, `media`, `font` and other front-end resource files, we don't need to separate them.
- Enable `source map` to locate the problem.
- Use the `devServer` configuration provided by `webpack` for local development.
- Don't compress code.
- Don't use unnecessary `loader` and `plugins`.
- ...

Development environment configuration is also different from person to person, this is what I do. But we should remember that development environment, configuration should be kept simple, in order to let us see the results faster, improve development efficiency!

## Development environment configuration
### First experience
Through the previous explanation, I believe you are familiar with the basic configuration of `webpack`:
- Handling `css, less`, front-end resources and other files
- Compiling `es6+` syntax and `api`.
- Processing `html` files

have mastered the relevant configuration methods, and we also summarized a [basic configuration](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-07), not too clear students can look at the first, this basic configuration is still quite important.

ok, then we first use [learn-08](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-08) this case to experience how to do local development debugging. In order to be as close as possible to our real-life development projects, the case will:
- refer to the image in `html`.
- use `less` to reference an image
- use `es6+` in `js` to load images dynamically.
- **Use the `devServer` configuration item**.

> For ease of reading, I'm not going to put the source code in the article, you can go to [github](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-08) to see it.

Then perform the following steps:
1. for semantic purposes, we name the `webpack` configuration file `webpack.dev.js`
2. Start `webpack` on the command line - `npm start`.
```npm
"scripts": {
    "start": "webpack server --config ./webpack.dev.js"
}
```

Run the results:

![devserve.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7cjo97ew3a46o1uolel.png)


We will find:
- After the file compilation and processing is complete, no file is outputted
- At this point, an internal server (`http://192.168.0.196:8080/`) is turned on. If we are on the same network as our computer, we can access this link from our phone and see the results. This is very efficient for debugging `H5` development (this is mainly due to the `deveServer` configuration item, which will be explained later on)
- The page is updated in real time after we change the code

The above is pretty much what we want for efficient development.

### Analyzing
After the initial experience, let's take a look at how to configure the development environment based on the development environment configuration ideas summarized above:

- Parsing styles does not require separating out the `css` file; we insert the parsed styles directly into the `<header />`:
```js
module: {
    rules: [
        {
            test: /.(css|less)$/, 
            use: [
                'style-loader',
                'css-loader',
                'less-loader'
            ]
        },
    ]
},
```
- For `js`, `img`, `media`, `font` and other front-end resource files, there is no need to subpackage:
```js
output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name]-[chunkhash:5].js',
},
module: {
    rules: [
        {
            test: /\.(png|svg|jpg|jpeg|gif)$/i,
            type: 'asset',
            parser: {
                dataUrlCondition: {
                    maxSize: 1024 * 3 // When the image size is < 3kb it will be converted to base64
                }
            },
            generator: {
                filename: '[name]-[hash:5][ext]' // Set the name of the output file
            }
        },
        {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
                loader: 'babel-loader'
            }
        }
    ]
},
```
- Enable `source map` to make it easier to locate the problem; and not compress the code:
```js
{
    mode: 'development',
    devtool: 'inline-cheap-module-source-map'
}
```
- Use the `devServer` provided with `webpack` for local development:
```js
devServer: {
    static: {
        directory: path.join(__dirname, 'static'),
    },
},
```
- No other extra `plugins` were installed, only the `plugin` that parses `html` was installed because it parses `html`:
```js
plugins: [
    new HtmlWebpackPlugin({
        filename: path.resolve(__dirname, './dist/[name].html'),
        template: path.resolve(__dirname, './src/index.html'),
        title: 'webpack.dev.config',
    })
],
```

If we compare the base configuration, we will find that the **development environment configuration is almost the same as the base configuration, except that the `devServer` configuration item** is used more to enable local server debugging:

```diff
// webpack.dev.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
+   mode: 'development',
+   devtool: 'inline-cheap-module-source-map',
    entry: {
        index: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: '[name]-[chunkhash:5].js',
    },
    module: {
        rules: [
            {
                test: /.(css|less)$/, 
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 1024 * 3 // When the image size is < 3kb it will be converted to base64
                    }
                },
                generator: {
                    filename: '[name]-[contenthash:5][ext][query]' // Set the name of the output file
                }
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: path.resolve(__dirname, './dist/[name].html'),
            template: path.resolve(__dirname, './src/index.html'),
            title: 'webpack.dev.config',
        })
    ],
+   devServer: {
+       static: {
+           directory: path.join(__dirname, 'static'),
+       },
+   }
}
```

## devServer

Remember our previous case, how we ran our code? We ran it with our own editor (`vscode`) to see the results after we had packaged it. If we were that inefficient, we wouldn't be able to go live with our project.

That's why `webpack` provides a very user-friendly configuration item called `devServer`.
> To use it, we need to install the `webpack-dev-server` dependency `npm i webpack-dev-server -D`.

From such a semantic name, we know that it must be a configuration that provides some kind of service in the development environment.

The service is that this will enable a local service that will compile our configured `webpack`, in real time, and save it in memory, so that we can easily debug our code, which serves the purpose of our local development needs to be efficient very well.

So this configuration item, **only used for our development environment configuration.**

## devServer.static
> Documentation Explanation: tells the server where to serve content from. This is only necessary if you wish to serve static files

### Misinterpretation

The `devServe` configuration item is actually written in quite a bit of detail in the documentation, so here's a potentially confusing one - `devServe.static`.

A lot of people use `webpack` and copy and paste, so it's common to see configurations like the following:
```js
output: {
    path: path.resolve(__dirname, '. /dist'), .
}
devServer: {
    static: {
      directory: path.join(__dirname, 'dist'), }
    }
}
```

Because the folder we set up for output after packaging is generally `dist`, the `devServe.static` configuration item specifies that the directory of static files to be served is also `dist`. So, we would naturally assume that the contents of the local server access opened by `webpack` at this point would be the contents inside the `dist` folder.

Therefore, we would incorrectly conclude that `devServe.static` serves to set the directory that is accessed by the local server when `webpack` is turned on.

However, we can look at [dev.config.js](https://github.com/limingcan562/learn-webpack-5/blob/main/learn-08/webpack.dev.js) above, where we set the name of the folder we output after packaging to ` dist`, and the directory set by `devServe.static` is `static`. If we follow our misinterpretation above, we should have a `404` or a blank page on our server at this point due to the difference in the two folder settings, but we can still run our service.

**This means that `devServe.static` is not setting the directory where the packaged output folder is accessed when `webpack` is turned on for the local server.**

### Correctly understood

#### Analysis
Let's move on to the [learn-08](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-08) case (the `static` folder holds the `logo.png` image inside).

We previously wanted to reference the image in question inside ``js``, which we typically do:
```js
// index.js
const img = new Image();
img.src = require('Your image path');
```

We normally `require` the image **according to the relative path**, which is equivalent to a module, so the image will be compiled by `webpack` (adding `hash` or converting it to `base64`), and then written to the final directory (`output.path`) that we have set up.

But when we set up:
```js
devServer: {
    static: {
      directory: path.join(__dirname, 'static'),
    }
}
```
After opening the local server, this line prompts:
> Content not from webpack is served from 'your path/static' directory

First of all, this means that the `static` directory we set up will not be processed and packaged by `webpack`, so the resources in this folder will not be parsed and compiled by `webpack`, and therefore it will not be `hashed` or converted to `base64`.

Let's take a look at how `index.js` from [learn-08](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-08) uses the resources in `static`:
```js
// index.js
loadImage('./logo.png').then(img => {
    console.log('I am from index.js');
    img.width = 120;
    document.querySelector('#js_content .img_content').append(img);
});
```
We will see that we are introducing the image directly using the `./` way to bring in the image, indicating that `webpack` has a local server turned on that will map the contents of the `static` directory we set up **to the root directory**.

> If you've used `vue-cli` before, you'll appreciate that this is what the `public` folder in `vue-cli` does.

We can access the contents of the set `devServer.static.directory` folder in the following way:
- Normally `devServer.static.directory` is mapped to the root directory, so we access it using `http://[devServer.host]:[devServer.port]/[devServer.static.directory]`
- If you want to change the access path, you can add `devServer.static.publicPath` configuration. In this case, you can use `http://[devServer.host]:[devServer.port]/[devServer.static.publicPath]/[devServer.static.directory]` for accessing

#### Summary
After analyzing the explanation above, we can conclude that the correct understanding of `devServer.static` should be:
- It is a directory for static resources that are not compiled by `webpack` (e.g., images, third-party resources, etc.), so it won't be `hashed` or `base64`; it is a directory.
- When opening a `webpack` local server, it is usually mapped to the root directory of our project (you can change the access directory via `devServe.static.publicPath`)
- Since it's not compiled and packaged by `webpack`, we usually end up copying `devServer.static.directory` to the root of `output.path` with `copy-webpack-plugin`
- It functions much like the `public` folder inside `vue-cli`, which we can use when we develop via `./` or `../` to access that resource (depending on the directory relationship).

## Core
Okay, so we know from the above explanation of the development environment configuration and `devServer`:
- The development environment configuration is kept simple, so resources such as `img`, `js`, etc. are mapped directly to the root directory (`css` is inserted into `<head />`).
- The development environment `webpack` also maps `devServer.static.directory` to the root directory when the local server is enabled.

So after running the development environment configuration, our structure directory is roughly as follows:

```tree
dist
├── ecj-cli-b6fa8.png
├── index-c466b.js
├── index.html
└── logo.png
```

Configuration varies from person to person, **the most important thing is that after we configure the development environment, production environment, the compilation and packaging of output content, directory, a good understanding of the structure of the mind, which is very important**.

> It is very important that we have a good understanding of the directory structure of the compiled and packaged output, which is very important.

We can see the structure of the development environment by the following method:
- `devServer.devMiddleware.writeToDisk: true`
- `http://[devServer.host]:[devServer.port]/webpack-dev-server`
- Chrome `F12 -> Sources`

## Finally
- This article learned how to configure the development environment, the next article will explain the configuration of the production environment before some of the preparations, so that we can better learn the production environment configuration
- The cases covered in the article have been uploaded to [github](https://github.com/limingcan562/learn-webpack-5), and you are really welcome to `star` or `fork` them.
- You can support me by following my [github](https://github.com/limingcan562)!

Regarding the content of the article, if you have similarities and differences, feel free to discuss them in the comments section!