You'll be more productive learning webpack with these methods
Tags: webdev, javascript, beginners, tutorial
Published: 2023-07-11 09:42:00

## Preface

As explained in the previous article - [Take your webpack to the next level by understanding these concepts](https://dev.to/limingcan562/take-your-webpack-to-the-next-level-by-understanding-these-concepts-h2p), we have a general understanding:
- Some of the concepts that arise in using `webpack` process
- Combined with the concepts of the packaging process, we analyzed the `webpack` packaging process, so that we can have a general understanding of the packaging process.

We all know that in the actual development process, we use `webpack` only to finally output `css`, `img`, `js`, `html` and other front-end resources that the browser can run.  

So, in order to be more close to the actual situation, in this article, we will learn how to configure `webpack` with the purpose of **how to output `css`, `img`, `js`, `html` and other front-end resources.**

## Learning Outline
This article explains the following basic configuration:
- Handling `css, less` files
- Handling `img`, `font`, `audio` and other front-end resource files.
- Compiling `es6+` syntax and `API`.
- Processing `html` files

The `webpack` documentation is pretty detailed, so I don't want to copy and paste it and write it all over again, so it's better to just read the documentation.  

Learning can't be learned the hard way, there should be some methods and techniques. Therefore, I would like to **summarize some methods** in the process of explaining the configuration, so that we can better understand the configuration of `webpack`. In this way, some similar configurations, we can match them by looking at the documentation.

## 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 the article together with the side of the knock, so that the impression will be more profound!
- It's not easy to create, if you think it's helpful, welcome to star🌟!

## Handling `css, less` files
Let's start with the [learn-03](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-03) case to see how `webpack` handles style files.

We install the appropriate `loader` to parse them:
- `less-loader`: parses `less` into `css`.
- `css-loader`: parses `css` into valid modules recognized by `webpack`.
- `style-loader`: parses `css` and inserts it into `<header />`.

```npm
npm install less less-loader css-loader style-loader -D
```

Then configure it:
```js
// webpack.config.js
module.exports = {
    ...,
    module: {
        rules: [
            {
                test: /.(css|less)$/, 
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader',
                ],
            },
        ]
    }
}
```
The parsing of `loader` is performed in reverse order (or right-to-left), so this configuration will be performed in the following order (`less-loader` → `css-loader` → `style-loader`):
1. parses `less` into `css`.
2. pass the result to `css-loader`, which parses it into a valid module recognized by `webpack`.
3. passes the result to `style-loader`, which inserts the parsed styles into `<header />`) in the following order.

The `less`, `css`, and entry file code is:

```js
// index.less
*{
    margin: 0;
    padding: 0;
}
h1{
    color: red;
    margin-top: 20px;
}

/* index.css */
h2{
    color: green;
}

// index.js
import './assets/index.less';
import './assets/index.css';
```

Output:

![compile_style](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ae6zexgzt3nytkgeu1i.png)

We'll see that the style we wrote was processed successfully and inserted into the `<head/>`.

### Summary

We [previous post](https://dev.to/limingcan562/take-your-webpack-to-the-next-level-by-understanding-these-concepts-h2p) said:
- The resource files that we come in via `import` or `require`, or each file in our project, can be viewed as an individual module
- The role of the `Loader` is to convert these modules into valid modules that `webpack` recognizes.

`webpack` humanely exposes the `module` configuration item, which is specifically designed to configure the relevant `loader` to parse the corresponding `module`. So if we want to parse some module used in our source code, we should:
- download the appropriate `loader` first
- **Find the `module` configuration directly** and configure it according to the `loader` usage.

So, we can try to configure our own `.scss` or `.txt` files according to the above summarized methods and ideas.

---

OK, we successfully got the styles (`css`) we need for our project, next we will see how to deal with images (`img`) and other front-end resource files.

## Handling front-end resource files
There are a lot of front-end resource files, here, we roughly divide them into two categories first:
- Common class: `img`, `font`, `video` and so on.
- Special category: `.csv`, `.xml` and so on.

These resources are also modules as long as they are `imported` or `required` into our source code. Since they are modules, we need to install the appropriate `loader` to parse them, and then configure them in the `module` configuration.

Prior to `wepack5`, **For resources of commonly used classes**, we generally needed to install the following `loader` to parse them:
- `raw-loader`: parses files into strings
- `file-loader`: parses files coming in from `import/require()` into a fully referenced `url` and sends the file to the output directory
- `url-loader`: can convert files to `base64`.

We often use `file-loader` and `url-loader`. This is because common resources are often introduced into our projects as `url`, or converted to `base64` in order to reduce `http` requests.

But **after `webpack5`, `webpack` has built-in the functions of the above several `loaders`, so we can no longer need to install the above `loaders`; we can directly use `webpack`'s own [asset module](https://webpack.js.org/guides/asset-modules/).**

The resource module built in after `webpack5`:
- `asset/source`: equivalent to `raw-loader`.
- `asset/resource`: equivalent to `file-loader`.
- `asset/inline`: equivalent to a `url-loader`.
- `asset`: more flexible, has both `asset/resource` and `asset/inline`. If you set a file size limit, the file will be converted to `base64` if it doesn't exceed the limit; if you don't set a size limit, it works the same as `file-loader`.

> *This is why I love `webpack5` so much, because we don't have to install so many messy `loaders`.*

Let's use the case of [learn-04](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-04) to see how we can use the resource module to parse our front-end resources.

Let's add the configuration from the previous chapter on parsing styles together and use the image resources in `less` and `js` respectively.

We have two images:
- `preview.gif`: size `349kb`
- `ecj-cli.png`: size `4kb`.

```js
// webpack.config.js
module.exports = {
 ...,
 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 * 10 // // When the image size is < 10kb it will be converted to base64
                    }
                },
                generator: {
                    filename: '[name][ext]' // Set the name of the output file
                }
            }
        ]
    }
}
```

Entry file:
```js
// index.js
import './assets/index.less';

const img = new Image();
img.src = require('./assets/ecj-cli.png');
img.width = 500;
document.querySelector('#js_logo').append(img);
```

Style:
```css
.bg_logo{
    width: (1434/3px);
    height: (1212/3px);
    background-image: url('./preview.gif');
    background-size: 100%;
}
```

Output results:

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

We'll see that the style and `js` both process the image successfully:
- `preview.gif` is larger than `10kb`, so instead of being converted to `base64`, a full reference path is returned (equivalent to `file-loader`)
- `ejc-cli.png` is smaller than `10kb` so it was converted to `base64` (equivalent to `url-loader`)

> The gif above is a command line tool [ejc-cli](https://github.com/limingcan562/ejc-cli) that I developed that is very useful in everyday projects. It can convert the `Excel` data collected by the docking staff into `json` data in a certain format which is needed by our code. It is very convenient for us to develop and maintain the data, and also to interface with other people.  
>
> If you are interested, you can check out 👉🏻 [learn about ejc-cli](https://github.com/limingcan562/ejc-cli)

### Summary
Resources that are imported into our source code via `import` or `require` are also modules. So you need to download the appropriate `loader` and configure it in the `module` configuration.

For common resources (`img`, `font`, `video`, `audio`, etc.):
- Because they are commonly used, `webpack5` has built-in functionality to handle them, so that we don't need to install an additional `loader`, and we use the `asset module` to manage them directly.
- **We generally use `asset/resource`, `asset/inline`, `asset` modules to manage common class resources**. Because the resources of our common classes are often introduced in our projects, we need to parse them to introduce the full `url`, or convert them to `base64` in order to reduce the number of `http` requests.

For special classes of resources (`.csv`, `.xml`, etc.):
- We can understand that because they are not commonly used, `webpack` does not have their functionality built in.
- Therefore, we need to install the appropriate `loader` to parse them. For example, if we want to parse a `.csv` file, we need to install the `csv-loader` to parse it.

We can try to configure ourselves to handle `.video` or `font` as summarized above.

---

OK, so far, we've got `css`, `img`. Let's see how we can get our `js` through `webpack` processing.

## Compiling `es6+`
To this day, `js` is evolving rapidly, and every year some new syntax or new `API` appears in order to improve development efficiency and code quality.

For example, in terms of syntax appeared:
- Arrow functions: `()=>`
- class syntactic sugar: `class People {}`
- Deconstructed assignment: `const [x, y] = [1, 2];`
- ...

On the `API` side again:
- `Promise`
- `Array.prototype.includes()`
- `Object.keys()`
- ...

But these new additions are not supported on some lower browsers, so we need to use `webpack` to compile our `es6+` into a version that these lower browsers can support.

Let's start with the [learn-05](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-05) case and see how we configure `webpack` to compile `es6+`.

We install the relevant dependencies:

```json
...
"devDependencies": {
    "@babel/core": "^7.22.8",
    "@babel/plugin-transform-runtime": "^7.22.7",
    "@babel/preset-env": "^7.22.7",
    "babel-loader": "^9.1.3",
    "webpack": "^5.88.1",
    "webpack-cli": "^5.1.4"
},
"dependencies": {
    "core-js": "^3.31.1"
},
```

Entry files. Uses `es6+` syntax such as `class`, `Promise`, `()=>{}` and `API`:

```js
// index.js
class People {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return Promise.resolve(this.name);
    }
}
const Lee = new People('Lee');
Lee.sayHi().then(name => console.log(`Hi, I am ${name}.`));
```
Configure `webpack`:

```js
// webpack.config.js
module.exports = {
    ...,
    module: {
        rules: [
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader'
                }
            }
        ]
    }
}
```
Configure `Babel`. Create a new `babel.config.js` file in the project root directory:

```js
// babel.config.js
const presets = [
    [
        '@babel/preset-env',
        {
            useBuiltIns: 'usage',
            corejs: {
                version: require('core-js/package.json').version,
                proposals: true
            }
        }
    ]
];
const plugins = [
    '@babel/plugin-transform-runtime',
];
module.exports = {presets, plugins}
```

Set the target browser to be compatible with. Add the `browserslist` field to `package.json`:

```json
"browserslist": [
    "ie 11"
]
```

The result of running it in `ie 11`:

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

We will find:
- The `es6+` we wrote runs successfully in `ie 11`.
- `Promise`, the `es6+` `API`, has also been added to `ie 11`.

It means that our compilation of `es6+` was successful.

### About `Babel`
In the configuration above, you will notice that if we want to compile `es6+` with `webpack`, we also need to add the `babel.config.js` file to the root directory, which is a much more complicated and troublesome step than dealing with other modules. This is because **compiling the core of `es6+` requires `Babel` to work with it**. `Babel` is a tool for compiling `es6+`.

Since this post is mainly about `webpack`, we'll just give a general mention of `Babel` here. Here are some common configurations for `Babel`.

- If the project we are developing is an **application** or a **large project**, we can configure it like this:
```js
const presets = [
    [
        '@babel/preset-env',
        {
            modules: false,
            useBuiltIns: 'entry', // or useBuiltIns: 'usage',
            corejs: {
                version: '3.27.2',
                proposals: true
            }
        }
    ]
];
const plugins = [
    '@babel/plugin-transform-runtime'
];
module.exports = {plugins, presets};

// index.js
// If use useBuiltIns: 'entry'
import 'core-js/stable'
```

- If we are trying to develop a **third party library**, we can configure it like this:

```js
// Babel配置
const presets = [
    [
        '@babel/preset-env',
        {
            modules: false
        }
    ]
];
const plugins = [
    [
        '@babel/plugin-transform-runtime',
        {
            corejs: {
                version: 3,
                proposals: true
            }
        }
    ]
];
module.exports = {plugins, presets};
```

### Summary
- If `js` is `imported` or `required` into our source code, then it is also a module. So we have to download the corresponding `loader` (`babel-loader`) and configure it in the `module` configuration.
- In addition, we need to configure `babel-loader` and, most importantly, `Babel` (`babel.config.js`).

So, if we want to "compile `es6+`", we need to configure `webapck`, and most importantly we need to configure `babel.config.js`. **If you don't want to just copy and paste the `Babel` configuration, you have to learn about `Babel`.**

---

Remember what we use `webpack` for? To output `css`, `img`, `js`, `html` and other front-end resources that the browser can run.

OK, so far we have processed `css`, `img`, `js` through `webpack`. Let's take a look at how we can get our `html` through `webpack` processing.

## Processing `html` files
In the above example, we want to see the effect in the browser, we need to:
- Create a new `html` file in the root directory.
- In the new `html` file, we need to bring in the packaged resource files manually.

This process is too troublesome, is there a way, we just provide a `html` template file, and then through the `webpack` compilation, it automatically help us to pack the packaged files into the good. This way, we can run `html` directly after packaging and see the effect in the browser.

`webpack` is so user-friendly, of course there is.

Let's start by using the [learn-06](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-06) example here to see how we can utilize `webpack` to achieve the results we described above.

Install the relevant plugins:
```npm
npm i html-webpack-plugin -D
```

This time, the `webpack` configuration is a little different:
We set the packaged `js` storage directory to be the `js` folder; and we add a `hash` value of `5` lengths to the packaged `js` name.

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

module.exports = {
    entry: {
        index: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: './js/[name]-[chunkhash:5].js',
        clean: true
    },
    
    plugins: [
        new HtmlWebpackPlugin({
            filename: path.resolve(__dirname, './dist/[name].html'),
            template: path.resolve(__dirname, './index.html'),
            title: 'HtmlWebpackPlugin',
        })
    ],
}
```

The structure of the packaged file is:

```tree
dist
├── index.html
└── js
    └── index-efbfd.js
```

The packaged `html` is as follows:

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

We will find:
- Our `html` and `js` are packaged and exported to the `dist` folder, which is very convenient for us to deploy (before we created `index.html` in the root directory, so there are only `img` and `js` files in the `dist` folder after packaging).
- The packaged `html` will automatically bring in our packaged `js` file for us.

### Analyzing

As we can see from the above example, if we want `webpack` to process `html` files, we need to configure it in `plugins` provided by `webpack`. Some of you may wonder why `html` is a module and why it is configured in `plugins` and not in `module`. Let's analyze it.

All the files of our project can be considered as a module, but whether we configure them in `module` or `plugins` depends on what we want to use them for. Generally, if we use the module in our own source code, we need a `Loader` to parse it**.

When we explained how to deal with `img` and `js` above, our purpose **was to parse these files in the source code**:
- `img`, which we need to convert to `base64`
- `js`, we need to compile `es6+` stuff into `es5` and below

And, our files are introduced into our project by `import` or `require()`, so of course we need the appropriate `Loader` to convert these modules.


But when dealing with `html` files, our purpose is not to parse the `html`, we **just want the packaged `html` to automatically reference our `js` and `img`, which is more like using the auto-introduction feature**; moreover, we don't introduce our `html` into our project, so of course we don't need the appropriate ` Loader` to parse it.

Remember from our [previous post](https://dev.to/limingcan562/take-your-webpack-to-the-next-level-by-understanding-these-concepts-h2p) we explained that `Loader` is used to transform the modules, and `Plugin` is used to enhance the `webpack` package when compiling.

In this case, we are dealing with the `html` file, and our goal is to make the packaged `html` automatically reference our `js` and `img`. So we need to configure the `plugins` configuration item to enhance the `webpack` packaging and compilation functionality.

Similarly, suppose we want to parse an `html` file in our project (in our project, `import html from '. /x.html'`), then we have to install the appropriate `loader` (`html-loader`) and configure it in the `module` configuration item.

### Summary

Whether to configure in the `module` or `plugins` configuration item depends on the purpose for which we are using these modules.

---

At this point, we have successfully configured `webpack` to get the `css`, `img`, `js`, and `html` we need for a project; this is also equivalent to learning the basic configuration of `webpack`.

You can use the methods summarized above to handle `sass`, `font`, `video` and other resources on your own to deepen your impression.

## Learning Summary

From the above explanation of some configuration items, we can have the following summary:
- To parse `css`, we need to install the `loader` and configure it in the `module`.
- To parse `img`, `font`, `video`, etc., we don't need to install the `loader` (`webpack` has built-in functionality to parse them). We usually use `asset/resource`, `asset/inline`, `asset` in `module` configuration to parse them.
- To parse `js`, we need to install the appropriate `loader` (`babel-loader`), configure it in the `module` configuration, and most importantly, learn about `Babel`.
- To parse the `html` file, we need to configure the `html-webpack-plugin` plugin in the `plugins` configuration field.
- Whether we configure it in the `module` or `plugins` configuration depends on what we want to do with the modules.

With the above learning, let's organize and output a complete `webpack` base configuration to make it more impressive:

```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
    entry: {
        index: './src/index.js'
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: './js/[name]-[chunkhash:5].js',
        clean: true
    },
    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 * 10 // When the image size is < 10kb 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'
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            filename: path.resolve(__dirname, './dist/[name].html'),
            template: path.resolve(__dirname, './src/index.html'),
            title: 'Webpack Basic Configuration',
        })
    ],
}
```

The full base configuration is at [learn-07](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-07), and we recommend that you take a look at the full version, only `webpack.config.js` is posted here.

## Finally
- I hope some of the methods summarized in this article for learning purposes will help you learn how to configure `webapck` better.
- Later articles will be in-depth configuration. In real projects, most of them are divided into development and production environments, so **we will learn how to configure `webpack` differently for different environments of development and production, which is closer to our real projects**.
- 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!