入门客AI创业平台(我带你入门,你带我飞行)
博文笔记

webpack的commonchunkplugin深度解析以及chunk和module内部结构

创建时间:2017-02-15 投稿人: 浏览次数:10738

因为最近自己也在不断研究这方面的内容,建议在github阅读

1.不添加commonchunkplugin打包的文件

我们的webpack配置如下:

 var webpack = require("webpack");

module.exports = {
    entry:  {
         main:"./src/index.js",
         main1:"./src/index1.js"
    },
    output: {
        path:     "builds",
        filename: "[name].entry.chunk.js",
        publicPath: "builds/",
    },
    plugins: [

    ],
    module: {
         loaders: [
         {
            test: /.js$/,
             enforce: "pre",
             loader: "eslint-loader"
          },
           {
                test:    /.js/,
                loader:  "babel-loader",
                include: __dirname + "/src",
            },
            {
                test:   /.scss/,
                loader: "style-loader!css-loader!sass-loader",
                // Or
               // loaders: ["style", "css", "sass"],
               //这里必须是loader后缀
            },
            {
                test:   /.html/,
                loader: "html-loader",
            }
        ],
    }
};

此时我们在build目录下生成如下的六个文件:

这里写图片描述

此时main和main1表示的都是入口文件打包的结果,而我们的其他文件都是通过require.ensure打包而成的。

2.添加commonchunkplugin打包的文件

更新webpack如下:

var webpack = require("webpack");

module.exports = {
    entry:  {
         main:"./src/index.js",
         main1:"./src/index1.js"
    },
    output: {
        path:     "builds",
        // filename: "[chunkhash].output.js",
        filename: "[name].entry.chunk.js",
        publicPath: "builds/",
    },
    plugins: [
         new webpack.optimize.CommonsChunkPlugin({
                // async: true,
                filename:"[name].bundle.js",
                children:true,
               //对main和main1的文件单独打包,main的chunks集合包含了两个文件,也就是require.ensure后的两个文件
                name:      ["main","main1"], // Move dependencies to our main file
                minChunks: 2, // How many times a dependency must come up before being extracted
            }),

    ],
    module: {
         loaders: [
         {
            test: /.js$/,
             enforce: "pre",
             loader: "eslint-loader"
          },
           {
                test:    /.js/,
                loader:  "babel-loader",
                include: __dirname + "/src",
            },
            {
                test:   /.scss/,
                loader: "style-loader!css-loader!sass-loader",
                // Or
               // loaders: ["style", "css", "sass"],
               //这里必须是loader后缀
            },
            {
                test:   /.html/,
                loader: "html-loader",
            }
        ],
    }
};

此时打包的结果为如下:

这里写图片描述

其中 name:[‘main’,’main1’]的配置表示,我们的main和main1这两个chunk(entry中配置)对应的子chunk中公有的模块会被打包到一起,也就是打包到main.entry.chunk.js和main1.entry.chunk.js中,但是输出文件不再是main.entry.chunk.js和main1.entry.chunk.js,而是filename:’[name].bundle.js’,也就是最后生成的main.bundle.js和main1.bundle.js。

3.当配置children后我们抽取公共模块的chunks集合

这时候在插件commonChunkPlugin中的抽取公共chunk的代码:

     commonChunks.forEach(function processCommonChunk(commonChunk, idx) {
                    let usedChunks;
                    if(Array.isArray(selectedChunks)) {
                        usedChunks = chunks.filter(chunk => chunk !== commonChunk && selectedChunks.indexOf(chunk.name) >= 0);
                    } else if(selectedChunks === false || asyncOption) {
                        usedChunks = (commonChunk.chunks || []).filter((chunk) => {
                            // we can only move modules from this chunk if the "commonChunk" is the only parent
                            return asyncOption || chunk.parents.length === 1;
                        });

                     //(1)
                     var util = require("util"); 
                    console.log("------------->commonChunk",util.inspect(commonChunk, {showHidden:true,depth:4})); 
                    //如果name=["main","main1"]那么表示以入口文件开始单独打包,此时的commonChunk就是我们的main.js和main1.js
                    //其chunks属性表示所有require.ensure的产生的chunk
                    } else {
                        if(commonChunk.parents.length > 0) {
                            compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it"s not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
                            return;
                        }
                        //如果found>=idx表示该chunk后续会作为一个独立的chunk来处理(独立打包),所以此处不做修改
                        //这里的chunks集合是包括所有entry中配置的和在name数组中配置的,如果entry中不存在这个chunk而name中存在,直接创建一个空的chunk!
                        usedChunks = chunks.filter((chunk) => {
                            const found = commonChunks.indexOf(chunk);
                            if(found >= idx) return false;
                            return chunk.hasRuntime();
                        });
                    }
                    let asyncChunk;
                    if(asyncOption) {
                        asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined);
                        asyncChunk.chunkReason = "async commons chunk";
                        asyncChunk.extraAsync = true;
                        asyncChunk.addParent(commonChunk);
                        commonChunk.addChunk(asyncChunk);
                        commonChunk = asyncChunk;
                    }
                    const reallyUsedModules = [];
                    if(minChunks !== Infinity) {
                        const commonModulesCount = [];
                        const commonModules = [];
                        usedChunks.forEach((chunk) => {
                            chunk.modules.forEach((module) => {
                                const idx = commonModules.indexOf(module);
                                if(idx < 0) {
                                    commonModules.push(module);
                                    commonModulesCount.push(1);
                                } else {
                                    commonModulesCount[idx]++;
                                }
                            });
                        });
                        const _minChunks = (minChunks || Math.max(2, usedChunks.length));
                        commonModulesCount.forEach((count, idx) => {
                            const module = commonModules[idx];
                            if(typeof minChunks === "function") {
                                if(!minChunks(module, count))
                                    return;
                            } else if(count < _minChunks) {
                                return;
                            }
                            if(module.chunkCondition && !module.chunkCondition(commonChunk))
                                return;
                            reallyUsedModules.push(module);
                        });
                    }
                    if(minSize) {
                        const size = reallyUsedModules.reduce((a, b) => {
                            return a + b.size();
                        }, 0);
                        if(size < minSize)
                            return;
                    }
                    const reallyUsedChunks = new Set();
                    reallyUsedModules.forEach((module) => {
                        usedChunks.forEach((chunk) => {
                            if(module.removeChunk(chunk)) {
                                reallyUsedChunks.add(chunk);
                            }
                        });
                        commonChunk.addModule(module);
                        module.addChunk(commonChunk);
                    });
                    if(asyncOption) {
                        for(const chunk of reallyUsedChunks) {
                            if(chunk.isInitial()) continue;
                            chunk.blocks.forEach((block) => {
                                block.chunks.unshift(commonChunk);
                                commonChunk.addBlock(block);
                            });
                        }
                        asyncChunk.origins = Array.from(reallyUsedChunks).map((chunk) => {
                            return chunk.origins.map((origin) => {
                                const newOrigin = Object.create(origin);
                                newOrigin.reasons = (origin.reasons || []).slice();
                                newOrigin.reasons.push("async commons");
                                return newOrigin;
                            });
                        }).reduce((arr, a) => {
                            arr.push.apply(arr, a);
                            return arr;
                        }, []);
                    } else {
                        usedChunks.forEach((chunk) => {
                            chunk.parents = [commonChunk];
                            chunk.entrypoints.forEach((ep) => {
                                ep.insertChunk(commonChunk, chunk);
                            });
                            commonChunk.addChunk(chunk);
                        });
                    }
                    if(filenameTemplate)
                        commonChunk.filenameTemplate = filenameTemplate;
                });

我们看看其中的chunk.hasRuntime函数:

hasRuntime() {
    if(this.entrypoints.length === 0) return false;
    return this.entrypoints[0].chunks[0] === this;
  }

我们看看chunk.entrypoints内部表示(见data.js下名字为main的chunk):

 entrypoints: 
   [ Entrypoint { name: "main", chunks: [ [Circular], [length]: 1 ] },
     [length]: 1 ]

所以只有顶级chunk才会有执行环境。我们顺便看看在commonchunkplugin的处理方式:

   //usedChunks是已经抽取了公共模块的chunk
   usedChunks.forEach(function(chunk) {
            chunk.parents = [commonChunk];
            chunk.entrypoints.forEach(function(ep) {
              ep.insertChunk(commonChunk, chunk);
              //在每一个移除了公共代码的chunk之前插入commonChunk
            });
            //每一个移除了公共chunk的chunk.entrypoints添加一个chunk
            commonChunk.addChunk(chunk);
          });

我们顺便也给出EntryPoint的代码:

class Entrypoint {
  constructor(name) {
    this.name = name;
    this.chunks = [];
  }
  unshiftChunk(chunk) {
    this.chunks.unshift(chunk);
    chunk.entrypoints.push(this);
  }
  insertChunk(chunk, before) {
    const idx = this.chunks.indexOf(before);
    if(idx >= 0) {
      this.chunks.splice(idx, 0, chunk);
    } else {
      throw new Error("before chunk not found");
    }
    chunk.entrypoints.push(this);
  }
  getFiles() {
    let files = [];
    for(let chunkIdx = 0; chunkIdx < this.chunks.length; chunkIdx++) {
      for(let fileIdx = 0; fileIdx < this.chunks[chunkIdx].files.length; fileIdx++) {
        if(files.indexOf(this.chunks[chunkIdx].files[fileIdx]) === -1) {
          files.push(this.chunks[chunkIdx].files[fileIdx]);
        }
      }
    }

    return files;
  }
}
module.exports = Entrypoint;

我们把上面的那几句代码添加注释来看看具体的打印内容:

   usedChunks.forEach(function(chunk,index) {
           var util = require("util"); 
               console.log("------------->before"+chunk.name,util.inspect(chunk.entrypoints, {showHidden:true,depth:2})); 
            chunk.parents = [commonChunk];
            chunk.entrypoints.forEach(function(ep) {
              ep.insertChunk(commonChunk, chunk);
            });
              var util = require("util"); 
          console.log("------------->end"+chunk.name,util.inspect(chunk.entrypoints, {showHidden:true,depth:2})); 
            commonChunk.addChunk(chunk);
          });

其中webpack中name的配置为 name: [“chunk”,’common1’,’common2’]。下面是控制台打印结果:

 ------------->beforemain [ Entrypoint {
    name: "main",
    //一开始我们的main这个chunk.entrypoints集合中只有一个Entrypoint对象,其为"main"
    //表示我们的chunk来自于那个入口文件
    chunks: 
    //Entrypoint的chunks集合表示这个Entrypoint产生了哪些chunks
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 1 ] },
  [length]: 1 ]
------------->endmain [ Entrypoint {
    name: "main",
    chunks: 
     [ Chunk {
       //这个chunk对应的入口文件产生了name为‘chunk’的chunk
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 2 ] },
  [length]: 1 ]
  //main1和main同样的道理
------------->beforemain1 [ Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 1 ] },
  [length]: 1 ]
------------->endmain1 [ Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 2 ] },
  [length]: 1 ]

//我们的chunk.js的入口文件是两个,分别为main和main1,通过调用两次(usedChunks有几个就是几次)commonChunk.addChunk来完成
//insertChunk修改了entryPoint的chunk,也同时修改了commonChunk的entrypoint
------------->beforechunk [ Entrypoint {
    name: "main",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 2 ] },
  Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 2 ] },
  [length]: 2 ]
------------->endchunk [ Entrypoint {
    name: "main",
    chunks: 
    //调用后每一个Entrypoint的chunks集合中都多了一个common1,因为其是commonchunk
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 3 ] },
  Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 3 ] },
  [length]: 2 ]
  //入口文件来自于common1
------------->beforecommon1 [ Entrypoint {
    name: "common1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 1 ] },
  Entrypoint {
    name: "main",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 3 ] },
  Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 3 ] },
  [length]: 3 ]
------------->endcommon1 [ Entrypoint {
    name: "common1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1003,
         name: "common2",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 2 ] },
  Entrypoint {
    name: "main",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1003,
         name: "common2",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1000,
         name: "main",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 4 ] },
  Entrypoint {
    name: "main1",
    chunks: 
     [ Chunk {
         id: null,
         ids: null,
         debugId: 1003,
         name: "common2",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1002,
         name: "common1",
         modules: [Object],
         entrypoints: [Circular],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1004,
         name: "chunk",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object] },
       Chunk {
         id: null,
         ids: null,
         debugId: 1001,
         name: "main1",
         modules: [Object],
         entrypoints: [Object],
         chunks: [Object],
         parents: [Object],
         blocks: [Object],
         origins: [Object],
         files: [Object],
         rendered: false,
         _removeAndDo: [Object],
         addChunk: [Object],
         addParent: [Object],
         entryModule: [Object] },
       [length]: 4 ] },
  [length]: 3 ]

所以,每一个Entrypoint.chunks表示这个Entrypoint产生了多少个chunks!chunk.entrypoints表示该chunk来自于哪些Entrypoint!从而形成一个网状结构!

3.1 commonChunkPlugin抽取之前的chunk

在(1)处,仅仅表示我们自己的main或者main1这个chunk的基本信息,包括modules等,还没有经过commonChunkPlugin进行抽取,具体内容参见data.js。

其中chunk的内部结构如下:

Chunk {
  id: null,
  ids: null,
  debugId: 1000,
  name: "main",
  //chunk对应的name
  modules: [],
  //该chunk来自于哪些module,main这个chunk来自于src/index.js,该module包含两个RequireEnsureDependenciesBlock
  entrypoints: 
   [ Entrypoint { name: "main", chunks: [ [Circular], [length]: 1 ] },
     [length]: 1 ],
  //入口文件为main:"./src/index.js",而entryPoint对应的chunk为对当前chunk的循环引用
 chunks:[],//当前chunk的子级chunks有哪些,如require.ensure都是当前chunk的子级chunk
 parents: [ [length]: 0 ],
 //当前chunk的父级chunk集合,没有经过commonChunkPlugin处理main是顶级chunk
 blocks: [ [length]: 0 ],
 //module.blocks表示模块包含的块RequireEnsureDependenciesBlock等的个数,chunk.block表示当前chunk包含的block的个数
 origins: 
 //当前chunk从哪些模块得到
   [ { module: 
        NormalModule {
          dependencies: [ [Object], [length]: 1 ],
          blocks: [ [Object], [Object], [length]: 2 ],
          variables: [ [length]: 0 ],
          context: "/Users/klfang/Desktop/webpack-chunkfilename/src",
          reasons: [ [length]: 0 ],
          debugId: 1000,
          lastId: null,
          id: null,
          portableId: null,
          index: 0,
          index2: 12,
          depth: 0,
          used: true,
          usedExports: true,
          providedExports: true,
          chunks: [ [Circular], [length]: 1 ],
          warnings: [ [Object], [length]: 1 ],
          dependenciesWarnings: [ [length]: 0 ],
          errors: [ [length]: 0 ],
          dependenciesErrors: [ [length]: 0 ],
          strict: true,
          meta: {},
          request: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
          userRequest: "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
          rawRequest: "./src/index.js",
          parser: 
           Parser {
             _plugins: [Object],
             options: undefined,
             scope: undefined,
             state: undefined },
          resource: "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
          loaders: [ [Object], [Object], [length]: 2 ],
          //module.fileDependencies: An array of source file paths included into a module. This includes the source JavaScript file itself (ex: index.js), and all dependency asset files (stylesheets, images, etc) that it has required. Reviewing dependencies is useful for seeing what source files belong to a module.
          //这个module没有引入相应的css/html/image等
          fileDependencies: 
           [ "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
             [length]: 1 ],
          contextDependencies: [ [length]: 0 ],
          error: null,
          _source: 
           OriginalSource {
             _value: ""use strict";

// var $ = require("jquery");

// $("body").html("Hello");


// import $ from "jquery";
// $("body").html("Hello");


// import Button from "./Components/Button";
// const button = new Button("google.com");
//  button.render("a");

//code splitting
if (document.querySelectorAll("a").length) {
    require.ensure([], function () {
        var Button = require("./Components/Button").default;
        var button = new Button("google.com");
        button.render("a");
    });
}

if (document.querySelectorAll("h1").length) {
    require.ensure([], function () {
        var Header = require("./Components/Header").default;
        new Header().render("h1");
    });
}",
             _name: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js" },
          assets: {},
          built: true,
          _cachedSource: null,
          issuer: null,
          building: undefined,
          buildTimestamp: 1487137260364,
          cacheable: true },
       loc: undefined,
       name: "main" },
     [length]: 1 ],
  files: [ [length]: 0 ],
  // An array of output filenames generated by the chunk. 
  //You may access these asset sources from the compilation.assets table.
  //表示这个chunk产生的输出文件,此处为顶级chunk没有输出文件产生
  _removeAndDo:{},
  addChunk:{},
  addParent:{},
  //入口模块
  entryModule: 
   NormalModule {
     dependencies: 
      [ ConstDependency {},
        [length]: 1 ],
     blocks: 
      [ RequireEnsureDependenciesBlock {
          dependencies: [ [Object], [Object], [Object], [length]: 3 ],
          blocks: [ [length]: 0 ],
          variables: [ [length]: 0 ],
          chunkName: null,
          chunks: [ [Object], [length]: 1 ],
          module: [Circular],
          loc: SourceLocation { start: [Object], end: [Object] },
          expr: 
           Node {
             type: "CallExpression",
             start: 313,
             end: 488,
             loc: [Object],
             range: [Object],
             callee: [Object],
             arguments: [Object] },
          range: [ 345, 486, [length]: 2 ],
          chunkNameRange: null,
          parent: [Circular] },
        RequireEnsureDependenciesBlock {
          dependencies: [ [Object], [Object], [Object], [length]: 3 ],
          blocks: [ [length]: 0 ],
          variables: [ [length]: 0 ],
          chunkName: null,
          chunks: [ [Object], [length]: 1 ],
          module: [Circular],
          loc: SourceLocation { start: [Object], end: [Object] },
          expr: 
           Node {
             type: "CallExpression",
             start: 543,
             end: 678,
             loc: [Object],
             range: [Object],
             callee: [Object],
             arguments: [Object] },
          range: [ 575, 676, [length]: 2 ],
          chunkNameRange: null,
          parent: [Circular] },
        [length]: 2 ],
     variables: [ [length]: 0 ],
     context: "/Users/klfang/Desktop/webpack-chunkfilename/src",
     reasons: [ [length]: 0 ],
     debugId: 1000,
     lastId: null,
     id: null,
     portableId: null,
     index: 0,
     index2: 12,
     depth: 0,
     used: true,
     usedExports: true,
     providedExports: true,
     chunks: [ [Circular], [length]: 1 ],
     warnings: [],
     dependenciesWarnings: [ [length]: 0 ],
     errors: [ [length]: 0 ],
     dependenciesErrors: [ [length]: 0 ],
     strict: true,
     meta: {},
     request: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
     userRequest: "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
     rawRequest: "./src/index.js",
     parser: 
      Parser {
        _plugins: {},
        options: undefined,
        scope: undefined,
        state: undefined },
     resource: "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
     loaders: 
      [ { loader: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js" },
        { loader: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js" },
        [length]: 2 ],
     fileDependencies: 
      [ "/Users/klfang/Desktop/webpack-chunkfilename/src/index.js",
        [length]: 1 ],
     contextDependencies: [ [length]: 0 ],
     error: null,
     _source: 
      OriginalSource {
        _value: ""use strict";

// var $ = require("jquery");

// $("body").html("Hello");


// import $ from "jquery";
// $("body").html("Hello");


// import Button from "./Components/Button";
// const button = new Button("google.com");
//  button.render("a");

//code splitting
if (document.querySelectorAll("a").length) {
    require.ensure([], function () {
        var Button = require("./Components/Button").default;
        var button = new Button("google.com");
        button.render("a");
    });
}

if (document.querySelectorAll("h1").length) {
    require.ensure([], function () {
        var Header = require("./Components/Header").default;
        new Header().render("h1");
    });
}",
        _name: "/Users/klfang/Desktop/webpack-chunkfilename/node_modules/babel-loader/lib/index.js!/Users/klfang/Desktop/webpack-chunkfilename/node_modules/eslint-loader/index.js!/Users/klfang/Desktop/webpack-chunkfilename/src/index.js" },
     assets: {},
     built: true,
     _cachedSource: null,
     issuer: null,
     building: undefined,
     buildTimestamp: 1487137260364,
     cacheable: true } }
}

3.2 commonChunkPlugin抽取公共代码抽取可视化分析

运行commonsChunkPlugin_Config中的example3代码,其中webpack的配置如下:

 var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
 module.exports = {
     entry: {
         main: process.cwd()+"/example3/main.js",
        main1: process.cwd()+"/example3/main1.js",
         common1:["jquery"],
         common2:["vue"]
     },
     output: {
         path: process.cwd()+"/dest/example3",
         filename: "[name].js"
    },
     plugins: [
         new CommonsChunkPlugin({
             name: ["chunk","common1","common2"],
             minChunks:2
         })
     ]
 };

我们看看commonchunkplugin中的处理方式(else部分):

    if(Array.isArray(selectedChunks)) {
          usedChunks = chunks.filter(function(chunk) {
            if(chunk === commonChunk) return false;
            //此时commonChunk的内容是已经存在于最终的文件中了,如果它不是手动创建的chunk
            //去掉下例的jquery,得到usedChunks集合
            return selectedChunks.indexOf(chunk.name) >= 0;
          });
        } else if(selectedChunks === false || asyncOption) {
          usedChunks = (commonChunk.chunks || []).filter(function(chunk) {
            // we can only move modules from this chunk if the "commonChunk" is the only parent
            //只是把一级子chunk的公共内容提取出来,如果有一个子chunk的父级chunk有两个那么不会被提取出来。
            return asyncOption || chunk.parents.length === 1;
          });
        } else {
          //如果当前的这个chunk有多个父级chunk,那么不会提取的
          if(commonChunk.parents.length > 0) {
            compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it"s not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
            return;
          }
          usedChunks = chunks.filter(function(chunk) {
            var found = commonChunks.indexOf(chunk);
            if(found >= idx) return false;
            return chunk.hasRuntime();
          });
        }

如果你运行如下命令webpack –profile –json > stats.json并按照本文方式查看,你会发现其实可视化后各个chunk的关系是如下所示的:

可视化结果

这样就很容易知道commonchunkplugin的处理方式了吧。

其中有一点你要特别注意,就是如果吧webpack配置修改为如下:

   new CommonsChunkPlugin({
             name: ["common1","common2","chunk"],
             minChunks:2
         })

此时我们继续可视化会得到如下的结果:

这里写图片描述

所以通过修改name数组中的元素的顺序往往会得到不同的chunk关系!但是需要注意一点,那就是我们的runtime执行环境会被抽取到最上级的chunk中,这里就是chunk.js:

/******/ (function(modules) { // webpackBootstrap
/******/  // install a JSONP callback for chunk loading
/******/  var parentJsonpFunction = window["webpackJsonp"];
/******/  window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/    // add "moreModules" to the modules object,
/******/    // then flag all "chunkIds" as loaded and fire callback
/******/    var moduleId, chunkId, i = 0, resolves = [], result;
/******/    for(;i < chunkIds.length; i++) {
/******/      chunkId = chunkIds[i];
/******/      if(installedChunks[chunkId])
/******/        resolves.push(installedChunks[chunkId][0]);
/******/      installedChunks[chunkId] = 0;
/******/    }
/******/    for(moduleId in moreModules) {
/******/      if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/        modules[moduleId] = moreModules[moduleId];
/******/      }
/******/    }
/******/    if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/    while(resolves.length)
/******/      resolves.shift()();
/******/    if(executeModules) {
/******/      for(i=0; i < executeModules.length; i++) {
/******/        result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/      }
/******/    }
/******/    return result;
/******/  };

/******/  // The module cache
/******/  var installedModules = {};

/******/  // objects to store loaded and loading chunks
/******/  var installedChunks = {
/******/    4: 0
/******/  };

/******/  // The require function
/******/  function __webpack_require__(moduleId) {

/******/    // Check if module is in cache
/******/    if(installedModules[moduleId])
/******/      return installedModules[moduleId].exports;

/******/    // Create a new module (and put it into the cache)
/******/    var module = installedModules[moduleId] = {
/******/      i: moduleId,
/******/      l: false,
/******/      exports: {}
/******/    };

/******/    // Execute the module function
/******/    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/    // Flag the module as loaded
/******/    module.l = true;

/******/    // Return the exports of the module
/******/    return module.exports;
/******/  }

/******/  // This file contains only the entry chunk.
/******/  // The chunk loading function for additional chunks
/******/  __webpack_require__.e = function requireEnsure(chunkId) {
/******/    if(installedChunks[chunkId] === 0)
/******/      return Promise.resolve();

/******/    // an Promise means "currently loading".
/******/    if(installedChunks[chunkId]) {
/******/      return installedChunks[chunkId][2];
/******/    }
/******/    // start chunk loading
/******/    var head = document.getElementsByTagName("head")[0];
/******/    var script = document.createElement("script");
/******/    script.type = "text/javascript";
/******/    script.charset = "utf-8";
/******/    script.async = true;
/******/    script.timeout = 120000;

/******/    if (__webpack_require__.nc) {
/******/      script.setAttribute("nonce", __webpack_require__.nc);
/******/    }
/******/    script.src = __webpack_require__.p + "" + chunkId + ".js";
/******/    var timeout = setTimeout(onScriptComplete, 120000);
/******/    script.onerror = script.onload = onScriptComplete;
/******/    function onScriptComplete() {
/******/      // avoid mem leaks in IE.
/******/      script.onerror = script.onload = null;
/******/      clearTimeout(timeout);
/******/      var chunk = installedChunks[chunkId];
/******/      if(chunk !== 0) {
/******/        if(chunk) chunk[1](new Error("Loading chunk " + chunkId + " failed."));
/******/        installedChunks[chunkId] = undefined;
/******/      }
/******/    };

/******/    var promise = new Promise(function(resolve, reject) {
/******/      installedChunks[chunkId] = [resolve, reject];
/******/    });
/******/    installedChunks[chunkId][2] = promise;

/******/    head.appendChild(script);
/******/    return promise;
/******/  };

/******/  // expose the modules object (__webpack_modules__)
/******/  __webpack_require__.m = modules;

/******/  // expose the module cache
/******/  __webpack_require__.c = installedModules;

/******/  // identity function for calling harmony imports with the correct context
/******/  __webpack_require__.i = function(value) { return value; };

/******/  // define getter function for harmony exports
/******/  __webpack_require__.d = function(exports, name, getter) {
/******/    if(!__webpack_require__.o(exports, name)) {
/******/      Object.defineProperty(exports, name, {
/******/        configurable: false,
/******/        enumerable: true,
/******/        get: getter
/******/      });
/******/    }
/******/  };

/******/  // getDefaultExport function for compatibility with non-harmony modules
/******/  __webpack_require__.n = function(module) {
/******/    var getter = module && module.__esModule ?
/******/      function getDefault() { return module["default"]; } :
/******/      function getModuleExports() { return module; };
/******/    __webpack_require__.d(getter, "a", getter);
/******/    return getter;
/******/  };

/******/  // Object.prototype.hasOwnProperty.call
/******/  __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

/******/  // __webpack_public_path__
/******/  __webpack_require__.p = "";

/******/  // on error function for async loading
/******/  __webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/ })
/************************************************************************/
/******/ ([]);
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。