webpack的commonchunkplugin深度解析以及chunk和module内部结构
来源:互联网 发布:淘宝兼职需要身份证吗 编辑:程序博客网 时间:2024/06/10 13:50
因为最近自己也在不断研究这方面的内容,建议在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\';\n\n// var $ = require(\'jquery\');\n\n// $(\'body\').html(\'Hello\');\n\n\n// import $ from \'jquery\';\n// $(\'body\').html(\'Hello\');\n\n\n// import Button from \'./Components/Button\';\n// const button = new Button(\'google.com\');\n// button.render(\'a\');\n\n//code splitting\nif (document.querySelectorAll(\'a\').length) {\n require.ensure([], function () {\n var Button = require(\'./Components/Button\').default;\n var button = new Button(\'google.com\');\n button.render(\'a\');\n });\n}\n\nif (document.querySelectorAll(\'h1\').length) {\n require.ensure([], function () {\n var Header = require(\'./Components/Header\').default;\n new Header().render(\'h1\');\n });\n}', _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\';\n\n// var $ = require(\'jquery\');\n\n// $(\'body\').html(\'Hello\');\n\n\n// import $ from \'jquery\';\n// $(\'body\').html(\'Hello\');\n\n\n// import Button from \'./Components/Button\';\n// const button = new Button(\'google.com\');\n// button.render(\'a\');\n\n//code splitting\nif (document.querySelectorAll(\'a\').length) {\n require.ensure([], function () {\n var Button = require(\'./Components/Button\').default;\n var button = new Button(\'google.com\');\n button.render(\'a\');\n });\n}\n\nif (document.querySelectorAll(\'h1\').length) {\n require.ensure([], function () {\n var Header = require(\'./Components/Header\').default;\n new Header().render(\'h1\');\n });\n}', _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; };/******/ })/************************************************************************//******/ ([]);
- webpack的commonchunkplugin深度解析以及chunk和module内部结构
- 89C51单片机内部结构深度解析
- UINavigationController和UITabbarController内部结构解析
- 使用webpack2和extract-text-webpack-plugin时遇到Chunk.entry was removed. Use hasRuntime()错误的解决方法
- 使用webpack2和extract-text-webpack-plugin时遇到Chunk.entry was removed. Use hasRuntime()错误的解决方法
- 解析Tomcat内部结构和请求过程
- 解析Tomcat内部结构和请求过程
- 解析Tomcat内部结构和请求过程
- 解析Tomcat内部结构和请求过程
- Set和Map的内部结构
- Set和Map的内部结构
- Set和Map的内部结构
- SSAS的存储原理以及内部结构
- DATA BLOCK内部结构的解析续二
- 对vector内部结构的解析(一)
- 对vector内部结构的解析(二)
- Ruby的module 以及 require 和 include
- strip 、 stripe 和chunk的概念
- JavaScript-基础(二)
- 冒泡排序
- Android 微信热修复 Tinker 接入过程及使用方法
- Java程序员面试题集(1-50)
- 图结构练习——BFS——从起始点到目标点的最短步数
- webpack的commonchunkplugin深度解析以及chunk和module内部结构
- python3+PyQt5 自定义窗口部件--创建复合窗口部件
- 4. Redis与Java的使用
- Activity启动过程分析
- Android项目之ImageLoader
- 嵌入式linux 保存参数的方法
- c或c++实现webService
- Project Euler 011 Largest product in a grid
- 第五届蓝桥杯JavaA组第三题