Take your webpack to the next level by understanding these concepts
Tags: webdev, javascript, beginners, tutorial
Published: 2023-06-26 15:03:27

## What is webpack?
It is a packaging tool:
- It can take the `es6+` `js`, `less`, `sass`, `img`, `font` and many other module resource files in our source code, and compile them through a series of processing to create front-end resources such as `css`, `js`, `html` that our browsers can recognize and run
- At the same time, it also provides many highly configurable features (`tree shaking`, `code-splitting`, `module hot-replacement`) to help us better optimize the management of our code and resources

## 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🌟!

## webpack.config.js
Let's take a look at the general framework of this configuration file to get an idea of what it looks like:

```js
// webpack.config.js
module.exports = {
  entry: {},
  output: {},
  resolve: {},
  module: {},
  plugins: [],
  optimization: {},
  devServer: {}
};
```
`webapck` of course also supports many configuration items, but we usually use the general on these few, other configuration items can see [documentation](https://webpack.js.org/configuration/)

### Attention:

`webpack` configuration file, the code inside is free to play around with, and the name of the configuration file doesn't have to be `webpack.config.js`, just make sure that **the final output is a `webpack` configured object**:

```js
// myconfig.js

// Various logic codes
...
...

// Just make sure to export a `webpack` configured object at the end
module.exports = {
    entry: {},
    output: {},
    ....
}
```

```npm
"build": "webpack --config myconfig.js"
```
### Optimization
All the configurations are written in one file, which is not good for our maintenance and development; and when we develop a project, there are multiple environments (local development environment, test environment, production environment), so we usually have a configuration file for different environments, and a configuration file that is common to all environments.  
We usually merge the "common configuration file" with the "different environment configuration file", and then `webpack` will run the merged configuration file.

Configuration files are generally available as follows:
- Common configuration for development and production environments (`webpack.common.js`)
- Development environment configuration (`webpack.dev.js`)
- Production environment configuration (`webpack.pro.js`)

> Subsequent articles will teach you how to do this configuration method.

## entry
It refers to the starting point where `webpack` starts parsing and building the dependency graph. We generally configure ``entry`` in the form of a `{key: value}` object, e.g:
```js
module.exports = {
 entry: {
   index: '. /src/index.js'.
   share: '. /src/share.js'.
 }
}
```

This means:
- We have two entry points in our project `index`, `share`
- `webpack` will start building the modules they depend on from the `index` and `share` portals, thus forming a dependency graph (more on this later)
- After packaging, the packaged file will be named with `key`.

## output
It's easy to understand, it sets the name of the compiled file after `webpack` packaging and where it should be output to.

Note that even if we set multiple `entry` entries, we can only specify one `output` configuration.


## Module
The resource files we bring in via `import` or `require`, or each file in our project, can be seen as a separate module. So, **it can be a `js` file, a `css` file, or any kind of resource such as an image**. So we often see the following statement in our development:
- `import 'index.css'`
- `import A from './x.js'`
- `import pic from './x.png'`
- ...

The combination of modules will in turn form a `Chunk`. `Chunk` is a very important concept and will be discussed in detail later.

Reference article: [Modules](https://webpack.js.org/concepts/modules/)


## Loader
As we know from above, each file in the project can be considered as a module, and since we have a variety of file types, we need something that can parse these modules into valid modules that `webpack` can recognize and add them to the dependency graph, and that something is `Loader`.

`webpack` is also kind enough to provide us with a `module` configuration item, which is specifically used to configure different `loaders` to parse different file types (modules). This is because `webpack` can only parse `js` and `json` files by default, so if we want to parse different types of files (modules), we have to install the corresponding `loader`: 

```js
// webpack.config.js
module.exports = {
 ...,
 modules: {
  rules: [
    // Parsing .txt files, using raw-loader
    { test: /.txt$/, use: 'raw-loader' },
  ],
 }
}
```

The `loader` has two properties:
- The `test` property, which identifies which files will be converted.
- The `use` property, which defines which `loader` should be used when doing the conversion.

What other `loaders` are available in `webpack` can be seen [here](https://webpack.js.org/loaders/)

## Plugin
`Loader` is used to transform modules, `Plugin` is used to enhance the functionality of `webpack` when it is packaged and compiled. So it generally has functions like packaging optimization, resource management, injection of environment variables, etc.

Because `webpack` is also kind enough to provide us with a `plugin` configuration item, we can just go to `plugin` and configure what features we want to enhance, for example, if we define some global variables directly in our project:

```js
// webpack.config.js
module.exports = {
 ...,
 plugins: [
  new webpack.DefinePlugin({
   AUTHOR_NAME: JSON.stringify('Lee')
  })
 ]
}

// index.js
console.log(AUTHOR_NAME); // Lee
```

You can check [here](https://webpack.js.org/plugins/) to see what other plugins are available.

## Dependency graph
When we have a file that depends on another file, `webpack` treats the file as having a direct "dependency".

As a simple example, let's say we have three files that have the following relationship:
```js
// main.js
import './index.js';

// index.js
import 'plugin.js'
console.log('I am from index.js');

// plugin.js
console.log('I am plugin.js');
```

Let's analyze:
1. `main.js`, `index.js`, `plugin.js` are equivalent to three modules;
2. then `main.js` introduces `index.js`, and `index.js` introduces `plugin.js`;
3. so all three files have a cross-reference relationship;
4. a graph with references is formed as follows: `main.js` → `index.js` → `plugin.js`

To summarize:

`webpack` will start with `entry` and use it as the starting point of the dependency graph; then analyze and process the `import` inside `entry` and keep recursively querying the dependencies similar to the above example, this process will finally form a graph with dependencies, this graph is the "dependency graph ".

`webpack` will then operate further based on this dependency graph.

## ⭐️ Chunk

**`Chunk` is a relatively important concept inside `webapck`, if we understand it, we will have a good understanding of the whole process of `webpack` packaging, code partitioning**.

### Explanation

**In the `webpack` packaging process, a module or a group of modules (any of the files in `webpack` that we mentioned above can be considered as a module) will be combined into a whole, then this whole can be treated as a `Chunk`**. Generally speaking, the `webpack` packaging process has several `Chunks` and will end up outputting several `js` files.

Let's go through the case [learn-01](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-01), which is the simplest configuration, the simplest code, so that we can better understand what `Chunk` is.

We have three `js` files:
- `index.js`: the entry file
- `async.js`: file for asynchronous introduction
- `vendors.js`: you can use it as some third-party dependency file

The file code and `webpack` configuration are as follows:
```js
// webpack.config.js
module.exports = {
    entry: {
        index: './src/index.js'
    },
    output: {
        filename: '[name]-bundle.js'
    }
}

// index.js
import './vendors';
import(/* webpackChunkName: "async" */ './async');
console.log('I am from index.js');

// async.js
console.log('I am from async.js');

// vendors.js
console.log('I am from vendors.js');
```

When we see this, we can first guess how many files are packed out.

### Analysis
Let's first analyze the packaged files and their structure:
```tree
dist
├── async-bundle.js
└── index-bundle.js
```
A total of two `js` files are output.

From the above, we know that
- Each file can be seen as a `module`
- In the `webpack` packaging process, a module or a group of modules will be combined into a whole, then this whole can be treated as a `Chunk`

We proceed to analyze:
- By looking at the `dist/index-bundle.js` file, we will find that **it contains `js` files** that are not introduced asynchronously: the entry files `index.js`, `vendors.js` (and some code that was added by `webpack` itself when it was packaged *runtime*). This means that the two modules `index.js` and `vendors.js` form a `Chunk`. Here we call it `chunk[initial]`;
- By looking at the `async-bundle.js` file, it only contains the code for `async.js`. This means that **the module introduced asynchronously is split into a separate `Chunk`**. Here we call this `Chunk` `chunk[no-initial]`
- Both `chunk[initial]` and `chunk[no-initial]` come from the same entry `index.js`, so the two `Chunks` combined can be seen as a `Chunk` group, or the entry files will form a whole `Chunk` group. We call it `Chunk[index]`

Okay, I believe that by now, you should have a general impression of `Chunk` formation, let's run through the process again.

When `webpack` is compiled, through our configuration:
1. it will first find `enrty`, there are several `entry`, it will use these `entry` to form a `Chunk` group (`Chunk[index]` in the example)
2. then analyze these `Chunk` group, the entry `js` and all the related dependency modules of this entry, to form a `chunk` (`chunk[initial]` in the example)
3. if there is an asynchronous introduction of the module, the module is a separate `Chunk` (`chunk[no-initial]` in the example)
4. finally package the output `chunk[initial]` (`index-bundle.js`), `chunk[no-initial]` (`async-bundle.js`)

In the above example, `chunk`, `module` are related as follows:

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

### Form
From the above analysis, we can know that `Chunk` has two forms:
- `initial`: the initial one. Our entry `js` and all the related dependency modules of this entry, combined into a collection, can be seen as a `Chunk`. (i.e. `chunk[initial]` composed of `index.js`, `vendors.js` above)
- `non-initial`: non-initial. Indicates that it is an asynchronously loaded module, if we use something like `import('. /A.js')`, this `js` will be split into a separate asynchronous `Chunk`. (i.e. the `chunk[no-initial]` composed of `async.js` above)

### How to generate Chunk

- With the `entry` configuration, a `Chunk` group is generated with the entry as a whole (this group will not be packed out, it will just form this `Chunk` group)
- Generate `initial` `Chunk` with our entry `js` and all related dependency modules of this entry
- Generate `non-initial` `Chunk` by asynchronous `import()`
- Generate other `chunk` through `webpack` powerful **code splitting**

Through the above explanation and analysis, I hope you can have a rough process of `Chunk` formation in your mind when you use `webpack` in the future, which is very helpful for us to use code splitting.

Reference article: [Revealing internal principles](https://webpack.js.org/concepts/under-the-hood/)

## Bundle
`Bundle` refers to all the products of `webpack` after packaging.  

If our `output` configuration output file directory is `dist`, our `Bundle` is all the products in the `dist` folder.

Generally speaking there are a few `Chunks` and a few `js` `bundle` files packaged out.

## Packing process

### A brief analysis
In order to better understand the concepts parsed above, let's take a look at the `webpack` packaging process and see in which processes the concepts above are reflected.

After we run `webpack` in the terminal, it will go through the following process:
1. read the configuration file we specified (`webpack.config.js`)
2. start with the entry `entry`, analyze our `Module` and recurse the dependencies between the modules of our entire project
3. Load the appropriate `Loader`, parse these `Modules` into valid modules recognized by `webpack` and add them to the `Dependency graph`.
4. the compilation process will trigger several events to execute the configured `Plugin`.
5. the analyzed modules are grouped to form `Chunk`.
6. according to the configuration file (`output`), the final `Bundle` is output

The above process can be seen as three phases:
- Initialization phase (process 1)
- Compilation phase (process 2 - process 5)
- Output phase (process 6)

> The actual packaging process of webpack is of course much more complex, and here, for better understanding, is simplified

### Experience
Let's start from the practical side, with the case [learn-02](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-02), and use the actual code to have a deeper experience to understand the `webpack` packaging process and the concepts involved.

Let's take a look at the [learn-02](https://github.com/limingcan562/learn-webpack-5/tree/main/learn-02) project structure:

```tree
learn-02
├── index.html
├── package-lock.json
├── package.json
├── project.config.js
└── src
    ├── assets
    │   └── style.less
    ├── index.js
    ├── plugin
    │   ├── common.js
    │   ├── index-vendors.js
    │   └── share-vendors.js
    └── share.js
```

Let's introduce the corresponding files:
- `index.html`: used to run our packaged files and see the results
- `project.config.js`: the `webpack` configuration file (a separate name to distinguish it from `webpack.config.js`)
- `style.less`: style
- `index.js`: entry file
- `share.js`: entry file
- `common.js`: holds the common methods, which will be referenced **twice** by the two entry files, respectively
- `index-vendors.js`: can be used as some dependencies of `index.js`
- `share-vendors.js`: can be used as some dependencies of `share.js`

Relevant code from the following projects:

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

After we see the configuration of `project.config.js`, we can later guess how many files will be output after packaging as well.

Okay, now let's start the analysis:

1️⃣ When we run `npm run build` in the terminal, `webpack` will read the file `project.config.js` that we specified

2️⃣ Starting with `entry`, we analyze our module. Our entry has two `index`, `share`, so this will form two `Chunk` groups: `Chunk[index]`, `Chunk[share]`, and recurse our module's corresponding dependencies.

3️⃣ `index.js` introduces `style.less`, so it will load the corresponding `Loader`, parse it into a valid module that `webpack` can recognize, and add it to the dependency graph. This results in two dependency graphs:
- A dependency graph consisting of the entry `index.js` and its dependencies (`index.js -> style.less, common.js, index-vendors.js`)
- A dependency graph consisting of the entry `share.js` and its dependencies (`share.js -> common.js, share-vendors.js`)

4️⃣ These modules are then grouped into:
- A `chunk[initial-index]` composed of the entry `index.js` and its dependencies;
- A `chunk[initial-share]` composed of the entry `share.js` and its dependencies

5️⃣ found that our configuration also splits `commonjs` independently using code splitting, so it forms a separate `chunk[initial-common]`

6️⃣ At this point, `webpack` has split into three `chunks`:
- `chunk[initial-index]`
- `chunk[initial-share]`
- `chunk[initial-common]`

7️⃣ Final output `Bundle` according to `output`

> Again, the actual packing process is certainly much more complex

## Finally
- This article analyzes and explains some of the concepts involved in `webpack`, especially the knowledge of `Chunk` is more important. After understanding `Chunk`, you will definitely have a better understanding of `webpack`. Hopefully, after reading this article, we will have a general process in mind when using `webpack`, and we will be able to tell how many `Chunk`s there are.
- The following article will start to teach you how to configure `webpack`, if you are interested you can stay tuned!
- 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!