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 ascss
,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: {}
};
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: {},
....
}
"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:
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 theindex
andshare
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' },
],
}
}
The loader
has two properties:
- The
test
property, which identifies which files will be converted. - The
use
property, which defines whichloader
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
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');
Let's analyze:
-
main.js
,index.js
,plugin.js
are equivalent to three modules; - then
main.js
introducesindex.js
, andindex.js
introducesplugin.js
; - so all three files have a cross-reference relationship;
- 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');
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
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 aChunk
We proceed to analyze:
- By looking at the
dist/index-bundle.js
file, we will find that it containsjs
files that are not introduced asynchronously: the entry filesindex.js
,vendors.js
(and some code that was added bywebpack
itself when it was packaged runtime). This means that the two modulesindex.js
andvendors.js
form aChunk
. Here we call itchunk[initial]
; - By looking at the
async-bundle.js
file, it only contains the code forasync.js
. This means that the module introduced asynchronously is split into a separateChunk
. Here we call thisChunk
chunk[no-initial]
- Both
chunk[initial]
andchunk[no-initial]
come from the same entryindex.js
, so the twoChunks
combined can be seen as aChunk
group, or the entry files will form a wholeChunk
group. We call itChunk[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:
- it will first find
enrty
, there are severalentry
, it will use theseentry
to form aChunk
group (Chunk[index]
in the example) - then analyze these
Chunk
group, the entryjs
and all the related dependency modules of this entry, to form achunk
(chunk[initial]
in the example) - if there is an asynchronous introduction of the module, the module is a separate
Chunk
(chunk[no-initial]
in the example) - 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:
Form
From the above analysis, we can know that Chunk
has two forms:
-
initial
: the initial one. Our entryjs
and all the related dependency modules of this entry, combined into a collection, can be seen as aChunk
. (i.e.chunk[initial]
composed ofindex.js
,vendors.js
above) -
non-initial
: non-initial. Indicates that it is an asynchronously loaded module, if we use something likeimport('. /A.js')
, thisjs
will be split into a separate asynchronousChunk
. (i.e. thechunk[no-initial]
composed ofasync.js
above)
How to generate Chunk
- With the
entry
configuration, aChunk
group is generated with the entry as a whole (this group will not be packed out, it will just form thisChunk
group) - Generate
initial
Chunk
with our entryjs
and all related dependency modules of this entry - Generate
non-initial
Chunk
by asynchronousimport()
- Generate other
chunk
throughwebpack
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:
- read the configuration file we specified (
webpack.config.js
) - start with the entry
entry
, analyze ourModule
and recurse the dependencies between the modules of our entire project - Load the appropriate
Loader
, parse theseModules
into valid modules recognized bywebpack
and add them to theDependency graph
. - the compilation process will trigger several events to execute the configured
Plugin
. - the analyzed modules are grouped to form
Chunk
. - according to the configuration file (
output
), the finalBundle
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
Let's introduce the corresponding files:
-
index.html
: used to run our packaged files and see the results -
project.config.js
: thewebpack
configuration file (a separate name to distinguish it fromwebpack.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 ofindex.js
-
share-vendors.js
: can be used as some dependencies ofshare.js
Relevant code from the following projects:
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 entryindex.js
and its dependencies; - A
chunk[initial-share]
composed of the entryshare.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 ofChunk
is more important. After understandingChunk
, you will definitely have a better understanding ofwebpack
. Hopefully, after reading this article, we will have a general process in mind when usingwebpack
, and we will be able to tell how manyChunk
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!
Regarding the content of the article, if you have similarities and differences, feel free to discuss them in the comments section!
Top comments (0)