有如下文件 a.js / b.js / c.js / d.js 以及 webpack.config.js, 其中 a.js 为入口文件,它们之间的依赖关系以下图,实心箭头表明异步加载。javascript
// a.js - 入口文件
import add from './b.js'
add(1, 2)
import('./c.js').then(del => del(1, 2))
// b.js
import mod from './d.js'
export default function add(n1, n2) {
return n1 + n2
}
mod(100, 11)
// c.js
import mod from './d.js'
mod(100, 11)
import('./b.js').then(add => add(1, 2))
export default function del(n1, n2) {
return n1 - n2
}
// d.js
export default function mod(n1, n2) {
return n1 % n2
}
// webpack.js
module.exports = {
entry: {
app: './src/a.js'
},
output: {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].bundle.[chunkhash:8].js',
publicPath: '/'
},
optimization: {
runtimeChunk: {
name: 'bundle'
}
},
}
复制代码
chunkGroup:一个 chunkGroup 能够包含多个 chunk,能够经过 chunks
字段看出它由哪些 chunk 组成。Webpack 会为每一个入口建立一个 entrypoint,下图就是入口 app: './src/a.js' 的 entrypoint,能够看出 entrypoint 就是一个 chunkGroup。 css
chunk:一个 chunk 能够包含多个 module,能够经过 _groups
字段看出它所属的 chunkGroup,经过 _modules
看出它由哪些 module 组成。Webpack 在为每一个入口建立 entrypoint 的同时,也会建立一个 chunk,以下图: java
module:资源文件,如 js / css / 图片 等,能够经过 _chunks
看出它所属的 chunk,经过 blocks
看出它异步加载的模块。Webpack 会将它封装成 NormalModule 对象,'./src/a.js' 对应的 NormalModule 对象以下图: node
block:在模块中异步加载的模块,好比:import('./c.js').then()
,Webpack 会将它封装成 ImportDependenciesBlock。在 a.js 中异步加载的 c.js 封装以后的结构以下: webpack
为了在后续的源码解析中更清晰的描述,使用以下简写。另外:本次源码解析着重于 module graph & basic chunk graph 的建立,多余的分支不讲,由于我也没看。web
NM('./src/a.js'):'./src/a.js' 模块文件封装以后的 NormalModule.chrome
chunk('app'):Webpack 为入口建立的 chunk.npm
chunkGroup('app'):Webpack 为入口建立的 chunkGroup.json
block('./c.js'):异步加载的模块 './c.js' 封装以后的 ImportDependenciesBlock.数组
Webpack 会为每一个异步加载的模块建立一个 chunk & chunkGroup,并关联它们。
chunk('./c.js'):为异步加载的模块 './c.js' 建立的 chunk.
chunkGroup('./c.js'):为异步加载的模块 './c.js' 建立的 chunkGroup.
// compilation.js
class Compilation {
...
seal () {
...
// 建立 chunk 以前的 hook
this.hooks.beforeChunks.call();
// 根据 addEntry 方法中收集到入口文件组成的 _preparedEntrypoints 数组
for (const preparedEntrypoint of this._preparedEntrypoints) {
const module = preparedEntrypoint.module; // 即 NM('./src/a.js')
const name = preparedEntrypoint.name; // 即 'app'
const chunk = this.addChunk(name); // 为每一个入口建立 chunk
const entrypoint = new Entrypoint(name); // 为每一个入口建立 entrypoint,它就是 chunkGroup
entrypoint.setRuntimeChunk(chunk); // 设置为 runtime chunk
entrypoint.addOrigin(null, name, preparedEntrypoint.request);
this.namedChunkGroups.set(name, entrypoint);
this.entrypoints.set(name, entrypoint);
this.chunkGroups.push(entrypoint);
// 创建 chunkGroup 和 chunk 之间的关系
GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
// 创建 chunk 和 module 之间的关系
GraphHelpers.connectChunkAndModule(chunk, module);
chunk.entryModule = module;
chunk.name = name;
this.assignDepth(module);
}
this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice()); //接下来的重点
// 对 module 进行排序
this.sortModules(this.modules);
// 建立 chunk 以后的 hook
this.hooks.afterChunks.call(this.chunks);
this.hooks.optimize.call();
while (
this.hooks.optimizeModulesBasic.call(this.modules) ||
this.hooks.optimizeModules.call(this.modules) ||
this.hooks.optimizeModulesAdvanced.call(this.modules)
) {
/* empty */
}
// 优化 module 以后的 hook
this.hooks.afterOptimizeModules.call(this.modules);
while (
this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
// 主要涉及到 webpack.config.js optimization 配置
this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
) {
/* empty */
}
// 优化 chunk 以后的 hook
this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
...
}
...
}
// GraphHelpers.js
/** * @param {ChunkGroup} chunkGroup the ChunkGroup to connect * @param {Chunk} chunk chunk to tie to ChunkGroup * @returns {void} */
GraphHelpers.connectChunkGroupAndChunk = (chunkGroup, chunk) => {
if (chunkGroup.pushChunk(chunk)) {
chunk.addGroup(chunkGroup);
}
};
/** * @param {Chunk} chunk Chunk to connect to Module * @param {Module} module Module to connect to Chunk * @returns {void} */
GraphHelpers.connectChunkAndModule = (chunk, module) => {
if (module.addChunk(chunk)) {
chunk.addModule(module);
}
};
复制代码
Webpack 会遍历配置文件中的入口,为每一个入口建立一个 chunk & chunkGroup,此时的 chunk 还没收集任何 NormalModule,包括入口文件对应的 NormalModule。
将 chunk 设为 runtimeChunk,当 Webpack 编译完成后,webpack runtime 代码会注入到 runtimeChunk。
调用 GraphHelpers.connectChunkGroupAndChunk()
和 GraphHelpers.connectChunkAndModule()
创建 chunk & chunkGroup 之间的关系,以及 chunk & 入口模块 之间的关系。这里还未涉及 chunk 和 入口模块依赖的模块的关系。
至此,Webpack 已经建立了 chunkGroup('app') & chunk('app'),chunk('app').modules = Set(NM('./src/a.js'))
接下来咱们重点看一下 this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
该函数主要实现了两块功能:
const iteratorDependency = d => {
// We skip Dependencies without Reference
const ref = this.getDependencyReference(currentModule, d);
if (!ref) {
return;
}
// We skip Dependencies without Module pointer
const refModule = ref.module;
if (!refModule) {
return;
}
// We skip weak Dependencies
if (ref.weak) {
return;
}
blockInfoModules.add(refModule);
};
const iteratorBlockPrepare = b => {
blockInfoBlocks.push(b);
// blockQueue push b(异步依赖),从而进入到下一次的内层循环
blockQueue.push(b);
};
// 本次 compilation 包含的全部 module
for (const modules of this.modules) {
blockQueue = [module];
currentModule = module;
while (blockQueue.length > 0) {
block = blockQueue.pop(); // 同步 module / 异步 block
blockInfoModules = new Set(); // 保存模块依赖的同步 module
blockInfoBlocks = []; // 保存模块依赖的异步 block
if (block.variables) {
iterationBlockVariable(block.variables, iteratorDependency);
}
// 在 blockInfoModules 中添加 dependencies 中的普通 module
if (block.dependencies) {
iterationOfArrayCallback(block.dependencies, iteratorDependency);
}
// 在 blockInfoBlocks 和 blockQueue 数组中添加异步 module,
// 这样异步 block 也会进入到内层循环,去获取异步 block 的依赖
if (block.blocks) {
iterationOfArrayCallback(block.blocks, iteratorBlockPrepare);
}
const blockInfo = {
modules: Array.from(blockInfoModules),
blocks: blockInfoBlocks
};
// blockInfoMap 上保存了每一个 module 依赖的同步 modules 及 异步 blocks
blockInfoMap.set(block, blockInfo);
}
}
复制代码
this.modules = [NM('./src/a.js'), NM('./b.js'), NM('./c.js'), NM('./d.js')]
第一次外层循环:blockQueue = [NM('./src/a.js')], currentModule = NM('./src/a.js')
第一次内层循环 - blockQueue pop NM('./src/a.js'),开始处理 NM('./src/a.js')。先忽略 if (block.variables)
,进入 if (block.dependencies)
,遍历 NM('./src/a.js').dependencies 执行 iteratorDependency,在 blockInfoModules 中添加 NM('./src/a.js') 的同步依赖;而后进入 if (block.blocks)
,遍历 NM('./src/a.js').blocks 执行 iteratorBlockPrepare,在 blockInfoBlocks 中添加 NM('./src/a.js') 的异步依赖,而且 blockQueue push 该异步依赖。此时 blockQueue & blockInfoMap 以下:
blockQueue = [Block('./c.js')];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
})
复制代码
第二次内层循环 - blockQueue pop Block('./c.js'),开始处理 Block('./c.js'),步骤同上,此时 blockQueue & blockInfoMap 以下,内层循环结束。
blockQueue = [];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
}, {
key: Block('./c.js'),
value: { modules: [NM('./c.js')], blocks: [] }
})
复制代码
第二次外层循环:blockQueue = [NM('./b.js')], currentModule = NM('./b.js')
第一次内层循环 - blockQueue pop NM('./b.js'),开始处理 NM('./b.js'),步骤同上,此时 blockQueue & blockInfoMap 以下:
blockQueue = [];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
}, {
key: Block('./c.js'),
value: { modules: [NM('./c.js')], blocks: [] }
}, {
key: NM('./b.js'),
value: { modules: [NM('./d.js')], blocks: [] }
})
复制代码
第三次外层循环:blockQueue = [NM('./c.js')], currentModule = NM('./c.js')
第一次内层循环 - blockQueue pop NM('./c.js'),开始处理 NM('./c.js'),步骤同上,此时 blockQueue & blockInfoMap 以下:
blockQueue = [Block('./b.js')];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
}, {
key: Block('./c.js'),
value: { modules: [NM('./c.js')], blocks: [] }
}, {
key: NM('./b.js'),
value: { modules: [NM('./d.js')], blocks: [] }
}, {
key: NM('./c.js'),
value: { modules: [NM('./d.js')], blocks: [Block('./b.js')] }
})
复制代码
第二次内层循环 - blockQueue pop Block('./b.js'),开始处理 Block('./b.js'),步骤同上,此时 blockQueue & blockInfoMap 以下:
blockQueue = [];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
}, {
key: Block('./c.js'),
value: { modules: [NM('./c.js')], blocks: [] }
}, {
key: NM('./b.js'),
value: { modules: [NM('./d.js')], blocks: [] }
}, {
key: NM('./c.js'),
value: { modules: [NM('./d.js')], blocks: [Block('./b.js')] }
}, {
key: Block('./b.js'),
value: { modules: [NM('./b.js')], blocks: [] }
})
复制代码
第四次外层循环:blockQueue = [NM('./d.js')], currentModule = NM('./d.js')
第一次内层循环 - blockQueue pop NM('./d.js'),开始处理 NM('./d.js'),步骤同上,此时 blockQueue & blockInfoMap 以下:
blockQueue = [];
blockInfoMap = Map({
key: NM('./src/a.js'),
value: { modules: [NM('./b.js')], blocks: [Block('./c.js')] }
}, {
key: Block('./c.js'),
value: { modules: [NM('./c.js')], blocks: [] }
}, {
key: NM('./b.js'),
value: { modules: [NM('./d.js')], blocks: [] }
}, {
key: NM('./c.js'),
value: { modules: [NM('./d.js')], blocks: [Block('./b.js')] }
}, {
key: Block('./b.js'),
value: { modules: [NM('./b.js')], blocks: [] }
}, {
key: NM('./d.js'),
value: { modules: [NM('./d.js'), blocks: [] }
})
复制代码
至此,咱们已经把本次 compilation 中全部模块的同步 & 异步依赖信息保存在 blockInfoMap 中。
// For each async Block in graph
/** * @param {AsyncDependenciesBlock} b iterating over each Async DepBlock * @returns {void} */
const iteratorBlock = b => {
// 1. We create a chunk for this Block
// but only once (blockChunkGroups map)
let c = blockChunkGroups.get(b);
if (c === undefined) {
c = this.namedChunkGroups.get(b.chunkName);
if (c && c.isInitial()) {
this.errors.push(
new AsyncDependencyToInitialChunkError(b.chunkName, module, b.loc)
);
c = chunkGroup;
} else {
// 经过 addChunkInGroup 建立新的 chunkGroup 及 chunk,并返回 chunkGroup
c = this.addChunkInGroup(
b.groupOptions || b.chunkName,
module, // 这个 block 所属的 module
b.loc,
b.request
);
chunkGroupCounters.set(c, { index: 0, index2: 0 });
blockChunkGroups.set(b, c);
allCreatedChunkGroups.add(c);
}
} else {
// TODO webpack 5 remove addOptions check
if (c.addOptions) c.addOptions(b.groupOptions);
c.addOrigin(module, b.loc, b.request);
}
// 2. We store the Block+Chunk mapping as dependency for the chunk
let deps = chunkDependencies.get(chunkGroup);
if (!deps) chunkDependencies.set(chunkGroup, (deps = []));
// 当前 chunkGroup 所依赖的 block 及 chunkGroup
deps.push({
block: b,
chunkGroup: c,
couldBeFiltered: true
});
// 异步的 block 使用建立的新的 chunkGroup
// 3. We enqueue the DependenciesBlock for traversal
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module: module,
chunk: c.chunks[0], // 获取新建立的 chunkGroup 中的第一个 chunk,即 block 须要被加入的 chunk
chunkGroup: c // 异步 block 使用新建立的 chunkGroup
});
};
...
const ADD_AND_ENTER_MODULE = 0;
const ENTER_MODULE = 1;
const PROCESS_BLOCK = 2;
const LEAVE_MODULE = 3;
...
const chunkGroupToQueueItem = chunkGroup => ({
action: ENTER_MODULE,
block: chunkGroup.chunks[0].entryModule,
module: chunkGroup.chunks[0].entryModule,
chunk: chunkGroup.chunks[0],
chunkGroup
});
let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse()
while (queue.length) {
while (queue.length) {
const queueItem = queue.pop();
module = queueItem.module;
block = queueItem.block;
chunk = queueItem.chunk;
chunkGroup = queueItem.chunkGroup;
switch (queueItem.action) {
case ADD_AND_ENTER_MODULE: {
// We connect Module and Chunk when not already done
if (chunk.addModule(module)) {
module.addChunk(chunk);
} else {
// already connected, skip it
break;
}
}
// fallthrough
case ENTER_MODULE: {
...
queue.push({
action: LEAVE_MODULE,
block,
module,
chunk,
chunkGroup
});
}
// fallthrough
case PROCESS_BLOCK: {
// get prepared block info
const blockInfo = blockInfoMap.get(block);
// Traverse all referenced modules
for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
const refModule = blockInfo.modules[i];
if (chunk.containsModule(refModule)) {
// skip early if already connected
continue;
}
// enqueue the add and enter to enter in the correct order
// this is relevant with circular dependencies
queue.push({
action: ADD_AND_ENTER_MODULE,
block: refModule, // 依赖 module
module: refModule, // 依赖 module
chunk, // module 所属的 chunk
chunkGroup // module 所属的 chunkGroup
});
}
// Traverse all Blocks
iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
if (blockInfo.blocks.length > 0 && module !== block) {
blocksWithNestedBlocks.add(block);
}
break;
}
case LEAVE_MODULE: {
...
break;
}
}
}
const tempQueue = queue;
queue = queueDelayed.reverse();
queueDelayed = tempQueue;
}
复制代码
初始化 queue = [$1]
$1: {
action: ENTER_MODULE(1),
block: NM('./src/a.js'),
module: NM('./src/a.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
第一次外层循环:
第一次内层循环 - queue pop $1,开始处理 chunk('app'),进入到 case ENTER_MODULE:
,忽略其余代码,直接看 queue.push({})
,push 以下对象:
$2: {
action: LEAVE_MODULE(3),
block: NM('./src/a.js'),
module: NM('./src/a.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
注意 case ENTER_MODULE:
没有 break,因此会接着进入 case PROCESS_BLOCK:
,上面获得的 blockInfoMap
在这用到了,首先遍历 NM('./src/a.js') 依赖的 modules - [NM('./b.js')],若是 chunk('app') 还未关联,就 push queue 以下对象:
$3: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./b.js'),
module: NM('./b.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
接着遍历 NM('./src/a.js') 依赖的 blocks - [Block('./c.js')],针对每个 block,建立并关联 chunkGroup & chunk,而后 queueDelayed.push 如下对象,queueDelayed 用于在第二次外层循环以前从新初始化 queue,后面会用到。至此第一次内层循环结束,此时 queue = [$2, $3], chunk('app').modules = Set(NM('./src/a.js'))
。
{
action: PROCESS_BLOCK(2),
block: Block('./c.js'),
module: NM('./src/a.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')]}
}
复制代码
第二次内层循环 - queue pop $3,进入到 case ADD_AND_ENTER_MODULE:
,关联 chunk('app') & NM('./b.js'),接着进入 case ENTER_MODULE:
,忽略其余代码,直接看 queue.push({})
,push 以下对象:
$4: {
action: LEAVE_MODULE(3),
block: NM('./b.js'),
module: NM('./b.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'), NM('./b.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
接着进入 case PROCESS_BLOCK:
,遍历 NM('./b.js') 依赖的 modules - [NM('./d.js')],若是 chunk('app') 还未关联, 就 push queue 以下对象.
$5: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'), NM('./b.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
因为 NM('./b.js') 没有依赖的 blocks,因此不用 push queueDelayed,至此第二次内层循环结束,queue = [$2, $4, $5], chunk('app').modules = Set(NM('./src/a.js'), NM('./b.js'))
。
第三次内层循环 - queue pop $5,进入到 case ADD_AND_ENTER_MODULE:
,关联 chunk('app') & NM('./d.js'),接着进入 case ENTER_MODULE:
,忽略其余代码,直接看 queue.push({})
,push 以下对象:
$6: {
action: LEAVE_MODULE(3),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { name: 'app', _groups: Set(Entrypoint), _modules: Set(NM('./src/a.js'), NM('./b.js'), NM('./d.js'))},
chunkGroup: Entrypoint{ chunks: [chunk('app')] }
}
复制代码
遍历 NM('./d.js') 依赖的 modules & blocks,因为二者都为空,因此没有 queue.push,至此第三次内层循环结束,queue = [$2, $4, $6], chunk('app').modules = Set(NM('./src/a.js'), NM('./b.js'), NM('./d.js'))
。
接下来的三次内层循环都是进入 case LEAVE_MODULE:
,先无论里面的逻辑。至此内层循环结束,此时 queue = [], chunk('app').modules = Set(NM('./src/a.js'), NM('./b.js'), NM('./d.js'))
。
使用上次外层循环中赋值的 queueDelayed 从新初始化 queue,queue = [$1],并将 queueDelayed 置空,开始关联第二个 chunk & modules。
$1: {
action: PROCESS_BLOCK(2),
block: Block('./c.js'),
module: NM('./src/a.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')]}
}
复制代码
第二次外层循环:
第一次内层循环 - queue pop $1,进入 case PROCESS_BLOCK:
,遍历 Block('./c.js') 依赖的 modules - [NM('./d.js')],若是 chunk('./c.js') 还未关联,就 push queue 以下对象:
$2: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./c.js'),
module: NM('./c.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')]}
}
复制代码
因为 Block('./c.js') 没有依赖的 blocks,因此不用 push queueDelayed,至此第一次内层循环结束,queue: [$2], chunk('./c.js').modules = Set(0)
。
第二次内层循环 - queue pop $2,进入 case ADD_AND_ENTER_MODULE:
,关联 chunk('./c.js') & NM('./c.js'),接着进入 case ENTER_MODULE:
,忽略其余代码,直接看 queue.push({})
,push 以下对象:
$3: {
action: LEAVE_MODULE(3),
block: NM('./c.js'),
module: NM('./c.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./c.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')] }
}
复制代码
接着进入 case PROCESS_BLOCK:
,遍历 NM('./c.js') 依赖的 modules - [NM('./d.js')],若是 chunk('./c.js') 还未关联,就 push queue 以下对象:
$4: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./c.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')] }
}
复制代码
接着遍历 NM('./c.js') 依赖的 blocks - [Block('./b.js')],针对每个 block,建立并关联 chunkGroup & chunk; 而后 queueDelayed.push 如下对象,至此第二次内层循环结束,此时 queue = [$3, $4], chunk('./c.js').modules = Set(NM('./c.js'))
。
{
action: PROCESS_BLOCK(2),
block: Block('./b.js'),
module: NM('./c.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')]}
}
复制代码
第三次内层循环 - queue pop $4,进入 case ADD_AND_ENTER_MODULE:
,关联 chunk('./c.js') & NM('./d.js'),接着进入 case ENTER_MODULE:,忽略其余代码,直接看 queue.push({}),push 以下对象:
$6: {
action: LEAVE_MODULE(3),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./c.js'), NM('./d.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./c.js')] }
}
复制代码
进入 case PROCESS_BLOCK:
,NM('./d.js') 没有依赖的 modules & blocks,至此第三次内层循环结束,此时 queue = [$3, $6], chunk('./c.js').modules = Set(NM('./c.js'), NM('./d.js'))
。 接下来的两次内层循环都是进入 case LEAVE_MODULE:
,先无论里面的逻辑。至此内层循环结束,此时 queue = [], chunk('./c.js').modules = Set(NM('./c.js'), NM('./d.js'))
。
使用上次外层循环中赋值的 queueDelayed 从新初始化 queue,queue = [$1],并将 queueDelayed 置空,开始关联第三个 chunk & modules。
$1: {
action: PROCESS_BLOCK(2),
block: Block('./b.js'),
module: NM('./c.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')]}
}
复制代码
第三次外层循环:
第一次内层循环 - queue pop $1,进入 case PROCESS_BLOCK:
,遍历 Block('./b.js') 依赖的 modules - [NM('./b.js')],若是 chunk('./b.js') 还未关联,就 push queue 以下对象:
$2: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./b.js'),
module: NM('./b.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(0)},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')]}
}
复制代码
因为 Block('./b.js') 没有依赖的 blocks,因此不用 push queueDelayed,至此第一次内层循环结束,queue: [$2], chunk('./b.js').modules = Set(0)
。
第二次内层循环 - queue pop $2,进入 case ADD_AND_ENTER_MODULE:
,关联 chunk('./b.js') & NM('./b.js'),接着进入 case ENTER_MODULE:
,忽略其余代码,直接看 queue.push({})
,push 以下对象:
$3: {
action: LEAVE_MODULE(3),
block: NM('./b.js'),
module: NM('./b.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./b.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')] }
}
复制代码
接着进入 case PROCESS_BLOCK:
,遍历 NM('./b.js') 依赖的 modules - [NM('./d.js')],若是 chunk('./b.js') 还未关联,就 push queue 以下对象:
$4: {
action: ADD_AND_ENTER_MODULE(0),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./b.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')] }
}
复制代码
因为 Block('./b.js') 没有依赖的 blocks,因此不用 push queueDelayed,至此第二次内层循环结束,queue: [$3, $4], chunk('./b.js').modules = Set(NM('./b.js'))。
第三次内层循环 - queue pop $4,进入 case ADD_AND_ENTER_MODULE:
,关联 chunk('./b.js') & NM('./d.js'),接着进入 case ENTER_MODULE:,忽略其余代码,直接看 queue.push({}),push 以下对象:
$5: {
action: LEAVE_MODULE(3),
block: NM('./d.js'),
module: NM('./d.js'),
chunk: { _groups: Set(ChunkGroup), _modules: Set(NM('./b.js'), NM('./d.js'))},
chunkGroup: ChunkGroup{ chunks: [chunk('./b.js')] }
}
复制代码
接着进入 case PROCESS_BLOCK:
,NM('./d.js') 没有依赖的 modules & blocks,至此第三次内层循环结束,此时 queue = [$3, $5], chunk('./b.js').modules = Set(NM('./b.js'), NM('./d.js'))
。 接下来的两次内层循环都是进入 case LEAVE_MODULE:
,先无论里面的逻辑。至此内层循环结束,此时 queue = [], chunk('./b.js').modules = Set(NM('./b.js'), NM('./d.js'))
。
至此循环结束,咱们获得了三个 chunkGroup,每一个 chunkGroup 关联的 chunk 分别是 chunk('app') / chunk('./c.js') / chunk('./b.js'),它们关联的 modules 以下:
chunk('app').modules = Set(NM('./src/a.js'), NM('./b.js'), NM('./d.js'))
chunk('./c.js').modules = Set(NM('./c.js'), NM('./d.js'))
chunk('./b.js').modules = Set(NM('./b.js'), NM('./d.js'))
复制代码
能够看到 NM('./d.js') 在三个 chunk 中都存在,难道最终生成的每一个 bundle 中都会打包 NM('./d.js') 么?固然不会,后续 webppack 还会优化 basic chunk graph,生成 chunk graph.
在 package.json 中添加一条 script
"debug": "node --inspect-brk ./node_modules/webpack/bin/webpack.js"
终端运行 npm run debug
,在浏览器地址栏输入 chrome://inspect/#devices
,界面以下:
点击 Open dedicated DevTools for Node,会打开 chrome 调试工具,开始调试 Webpack