DEV Community

Cover image for Take your webpack to the next level by understanding these concepts
Lee
Lee

Posted on • Updated on

Take your webpack to the next level by understanding these concepts

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:

  • 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:

// webpack.config.js
module.exports = {
  entry: {},
  output: {},
  resolve: {},
  module: {},
  plugins: [],
  optimization: {},
  devServer: {}
};
Enter fullscreen mode Exit fullscreen mode

webapck of course also supports many configuration items, but we usually use the general on these few, other configuration items can see documentation

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:

// myconfig.js

// Various logic codes
...
...

// Just make sure to export a `webpack` configured object at the end
module.exports = {
    entry: {},
    output: {},
    ....
}
Enter fullscreen mode Exit fullscreen mode
"build": "webpack --config myconfig.js"
Enter fullscreen mode Exit fullscreen mode

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:

module.exports = {
 entry: {
   index: '. /src/index.js'.
   share: '. /src/share.js'.
 }
}
Enter fullscreen mode Exit fullscreen mode

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

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:

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

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

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:

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

// index.js
console.log(AUTHOR_NAME); // Lee
Enter fullscreen mode Exit fullscreen mode

You can check here 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:

// 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');
Enter fullscreen mode Exit fullscreen mode

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, 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:

// 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');
Enter fullscreen mode Exit fullscreen mode

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:

dist
β”œβ”€β”€ async-bundle.js
└── index-bundle.js
Enter fullscreen mode Exit fullscreen mode

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

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

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, 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 project structure:

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
Enter fullscreen mode Exit fullscreen mode

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

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 Chunks 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!

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

Top comments (0)