Reprinted detailed explanation of the configuration and usage of CommonsChunkPlugin

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:


  1. The entry file (entry) configured in entry chunk webpack is chunk.
  2. The children chunk
    entry file and its dependent files are also chunks through code split (code split).

  3. The files created by commons chunk through CommonsChunkPlugin are also chunks.

CommonsChunkPlugin configurable properties:

  1. 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
  2. filename
    specifies the file name of the commons chunk
  3. 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
  4. 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:

  1. Do not separate third-party libraries and custom public modules
  2. Separate third-party libraries, custom public modules, and webpack running files, but they are in the same file
  3. 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:
insert image description here
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:
insert image description here
check the packaging information of webpack in the command line:
insert image description here
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:
insert image description here
Let’s check the webpack packaging information in the command line:
insert image description here
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:

  1. First extract the webpack running file separately
  2. 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:
insert image description here
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:
insert image description here
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:

  1. Number: How many chunks are commonly referenced by the module before it is extracted and becomes commons chunk
  2. 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
  3. 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:
insert image description here
Let’s check the webpack packaging information in the command line again, and the custom public modules are separated:
insert image description here
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:

  1. module: the current chunk and the modules it contains
  2. 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:
insert image description here
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.


  1. 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.
  2. 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:

  1. 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
  2. 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:

  1. hash is build-specific, that is, each compilation is different - suitable for the development stage
  2. 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

Guess you like

Origin blog.csdn.net/qq_36968599/article/details/122475377