Explain the configuration and usage of CommonsChunkPlugin in detail
- Introduction
- CommonsChunkPlugin configurable properties:
- Practical application
- children and async attributes
- Implementation of browser caching
- thank you
Introduction
CommonsChunkPlugin is mainly used to extract third-party libraries and public modules, to avoid the bundle file loaded on the first screen or the bundle file loaded on demand is too large, resulting in too long loading time, it is really a sharp tool for optimization.
Let me first talk about the types of chunks mentioned in CommonsChunkPlugin in various tutorials and documents. There are mainly the following three types:
The entry file (entry) configured in entry chunk webpack is chunk.- The children chunk
entry file and its dependent files are also chunks through code split (code split).
The files created by commons chunk through CommonsChunkPlugin are also chunks.
CommonsChunkPlugin configurable properties:
- name
can be the name corresponding to an existing chunk (generally refers to the entry file), then the common module code will be merged into this chunk; otherwise, a commons chunk named name will be created for merging - filename
specifies the file name of the commons chunk - chunks
specifies the source chunk, that is, specifies which chunks to find the public module from. When this option is omitted, the default is entry chunks - minChunks
can be either a number, a function, or Infinity. The specific usage and differences will be described below
children and async belong to asynchronous applications and are explained at the end.
It may be said that everyone will be confused, and the demo will be used to test the above attributes.
Practical application
The following demos mainly test the following situations:
- Do not separate third-party libraries and custom public modules
- Separate third-party libraries, custom public modules, and webpack running files, but they are in the same file
- Separate third-party libraries, custom public modules, and webpack running files separately, each in a different file
Do not separate third-party libraries and custom public modules
The initial structure of the project, the dist directory will be generated after packaging:
The content of each file in the src directory is very concise, as follows:
common.js
export const common = 'common file';
first.js
import {
common} from './common';
import $ from 'jquery';
console.log($,`first ${
common}`);
second.js
import {
common} from './common';
import $ from 'jquery';
console.log($,`second ${
common}`);
package.json file:
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rimraf dist && webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"rimraf": "^2.6.2",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.10.1"
},
"dependencies": {
"jquery": "^3.2.1"
}
}
webpack.config.js:
const path = require("path");
const webpack = require("webpack");
const config = {
entry: {
first: './src/first.js',
second: './src/second.js'
},
output: {
path: path.resolve(__dirname,'./dist'),
filename: '[name].js'
},
}
module.exports = config;
Then run npm run build on the command line. At this time, there are more dist directories in the project:
check the packaging information of webpack in the command line:
check first.js and second.js, and you will find that the commonly referenced common.js file and jquery are all It is definitely unreasonable to package it in. The public modules are packaged repeatedly and the volume is too large.
Separate third-party libraries, custom public modules, and webpack running files
At this time, modify webpack.config.js to add an entry file vendor and CommonsChunkPlugin plug-in to extract public modules:
webpack.config.js:
const path = require("path");
const webpack = require("webpack");
const packagejson = require("./package.json");
const config = {
entry: {
first: './src/first.js',
second: './src/second.js',
vendor: Object.keys(packagejson.dependencies)//获取生产环境依赖的库
},
output: {
path: path.resolve(__dirname,'./dist'),
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '[name].js'
}),
]
}
module.exports = config;
Check the dist directory, and a new vendor.js file has been added:
Let’s check the webpack packaging information in the command line:
By checking the vendor.js file, we can find jquery and common.js that are dependent on the first.js and second.js files All are packaged into vendor.js, along with webpack's running files. In general, our initial goal is achieved, extracting common modules, but they are all in the same file.
At this point, some people must hope that their vendor.js is pure and flawless, only contains third-party libraries, does not contain custom public modules and webpack running files, or hopes to include third-party libraries and public modules, but does not include webpack running files.
In fact, this idea is correct, especially separating the webpack running file, because the webpack running file will change every time you pack it. If you don’t separate the webpack running file, the hash value corresponding to vendor.js will change every time you pack it. It causes vendor.js to change, but in fact your third-party library has not changed. However, the browser will think that your original cached vendor.js is invalid, and you need to go to the server to get it again. In fact, it is just a change in the webpack running file. It's a shame to ask someone to reload~
OK, let's test it for this situation.
Separate third-party libraries, custom public modules, and webpack running files
Here we go in two steps:
- First extract the webpack running file separately
- Then separate the third-party library and custom public modules. There are two ways to use minChunks here, and you will know it later.
Extract the webpack running file
First, extract the webpack running file and modify the webpack configuration file:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor','runtime'],
filename: '[name].js'
}),
]
In fact, the above code is equivalent to the following:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '[name].js'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
filename: '[name].js',
chunks: ['vendor']
}),
]
The above two paragraphs of extracting the webpack running file code mean creating a commons chunk named runtime to extract the webpack running file, where the source chunks is vendor.js.
Check the dist directory, and a runtime.js file has been added, which is actually the running file of webpack:
check the packaging information of webpack in the command line again, and you will find that the volume of vendor.js has been reduced, indicating that webpack has been run The file is extracted:
However, there is a custom public module common.js in vendor.js. People only want vendor.js to have a third-party library that the project depends on (here is jquery). At this time, the attribute minChunks is introduced. .
minChunks can be set to numbers, functions, and Infinity. The default value is 2, which is not the number of entry files mentioned in the official document. The meaning of minChunks is explained below:
- Number: How many chunks are commonly referenced by the module before it is extracted and becomes commons chunk
- Function: Accepts (module, count) two parameters and returns a boolean value. You can perform your specified logic in the function to determine whether a module is extracted as a commons chunk
- Infinity: only takes effect when the entry chunks >= 3, used to separate custom public modules in third-party libraries
Extract third-party libraries and custom public modules
To separate the third-party library from vendor.js, there are two methods mentioned above.
the first method
MinChunks is set to Infinity, modify the webpack configuration file as follows:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor','runtime'],
filename: '[name].js',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
filename: '[name].js',
chunks: ['first','second']//从first.js和second.js中抽取commons chunk
}),
]
Check the dist directory, and a common.js file has been added:
Let’s check the webpack packaging information in the command line again, and the custom public modules are separated:
at this time, vendor.js is pure and flawless, only containing third-party libraries file, common.js is the custom public module, and runtime.js is the running file of webpack.
The second method
To separate them is to use minChunks as a function, let’s talk about the meaning of minChunks as two parameters of the function:
- module: the current chunk and the modules it contains
- count: the number of times the current chunk and the modules it contains are referenced
As a function, minChunks will traverse each entry file and its dependent modules, and return a Boolean value. If it is true, it means that the file (module.resource) currently being processed is merged into the commons chunk, and if it is false, it will not be merged.
Continue to modify our webpack configuration file, comment out the vendor entry file, and use minChunks as a function to realize that vendor only includes third-party libraries to achieve the same effect as above:
const config = {
entry: {
first: './src/first.js',
second: './src/second.js',
//vendor: Object.keys(packagejson.dependencies)//获取生产环境依赖的库
},
output: {
path: path.resolve(__dirname,'./dist'),
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: '[name].js',
minChunks: function (module,count) {
console.log(module.resource,`引用次数${
count}`);
//"有正在处理文件" + "这个文件是 .js 后缀" + "这个文件是在 node_modules 中"
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
filename: '[name].js',
chunks: ['vendor']
}),
]
}
The above code is actually to generate a commons chunk called vendor, so which modules will be added to the vendor? Just traverse the entry file and its dependent modules. If the module is a js file and is in node_modules, it will be added to the vendor. In fact, this is also a way for the vendor to keep only third-party libraries.
Let's check the packaging information of webpack in the command line again:
you will find that it is consistent with the result of setting minChunks to Infinity above.
children and async attributes
These two attributes are mainly used in code split (code split) and asynchronous loading.
When children 1.1 is specified as true, it means that source chunks are children chunks obtained by code splitting through entry chunks (entry file).
1.2 Children and chunks cannot be set at the same time, because they both specify source chunks.
1.3 children can be used to merge the shared modules of the children chunks created by the entry chunk into itself, but this will result in a longer initial loading time.- async
solves the problem that the initial loading time is too long when children: true are merged into the entry chunks themselves. When async is set to true, the commons chunk will not be merged into itself, but a new asynchronous commons chunk will be used. When the children chunk is downloaded, the commons chunk is automatically downloaded in parallel.
Modify the webpack configuration file and add chunkFilename, as follows:
output: {
...........
chunkFilename: "[name].[hash:5].chunk.js",
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor','runtime'],
filename: '[name].js',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
children: true,
async: 'children-async'
})
]
chunkFilename is used to specify the name of the asynchronously loaded module, and the commonly referenced modules in the asynchronously loaded module will be merged into async to specify the name, which is children-async.
It is too troublesome to change it to an asynchronous screenshot. Let me briefly explain: first and second are asynchronous loading modules, and they both refer to the common.js module. If you do not set this step:
new webpack.optimize.CommonsChunkPlugin({
children: true,
async: 'children-async'
})
Then the commonly referenced common.js are packaged into their respective modules, and then packaged repeatedly.
OK, after you set it up, it also depends on how the children's faces are divided:
- If children is true, the commonly referenced modules will be packaged and merged into a public module named children-async. When you lazy load first or second, load this and children-async public modules in parallel
- children is false, and the commonly referenced modules will be packaged into the app.bundle loaded on the first screen, which will cause the first screen to load too long, and don't use it, so it's better to set it to true
Implementation of browser caching
Let's talk about the difference in hash value first:
- hash is build-specific, that is, each compilation is different - suitable for the development stage
- chunkhash is chunk-specific, and is a hash calculated based on the content of each chunk—suitable for production
Therefore, in the production environment, the file name should be changed to '[name].[chunkhash]' to maximize the use of browser cache.
Finally, when writing this article, I have tested a lot of demos myself. Of course, it is impossible to post all of them, but I still hope that I will do more hands-on tests.
https://github.com/creeperyang/blog/issues/37
https://segmentfault.com/q/1010000008726439/revision#r4
https://segmentfault.com/q/1010000009070061
https://www.jianshu.com/p/2b81262898a4
thank you
Sincere thanks to the author: Yanglinxiao for his excellent article, which answered my confusion about "CommonsChunkPlugin".
https://segmentfault.com/a/1190000012828879