1. 简单回顾
这里我们先简单回顾下webpack载入资源的过程,
(1)命令行调用webpack,
即调用了~/.nvm/versions/node/v8.12.0/bin/webpack
,原身在这,
~/.nvm/versions/node/v8.12.0/lib/node_modules/webpack/bin/webpack.js
(2)webpack.js用require载入了webpack-cli,
require(path.resolve(
path.dirname(pkgPath),
pkg.bin[installedClis[0].binName]
));
~/.nvm/versions/node/v8.12.0/lib/node_modules/webpack-cli/bin/cli.js
(3)cli.js中加载了模块webpack,然后调用它
它会返回一个compiler,然后再调用compiler.run。
const webpack = require("webpack");
compiler = webpack(options);
compiler.run(compilerCallback);
(4)compiler.run的实现,在webpack模块的Compiler.js文件中
compiler.run调用了,this.compile(onCompiled);
compiler方法调用了,this.hooks.make.callAsync
,
this.hooks.make.callAsync(compilation, err => {
...
});
(5)hooks.make的实现在webpack模块的SingleEntryPlugin.js文件中
它调用了compilation.addEntry
,实现位于Compilation.js
compiler.hooks.make.tapAsync(
"SingleEntryPlugin",
(compilation, callback) => {
const { entry, name, context } = this;
const dep = SingleEntryPlugin.createDependency(entry, name);
compilation.addEntry(context, dep, name, callback);
}
);
(6)compilation.addEntry调用了this._addModuleChain
用于加载入口文件,及其相关的所有依赖(包括依赖的依赖),
全部加载完了之后,会进入this._addModuleChain的回调。
this._addModuleChain(
context,
entry,
module => {
this.entries.push(module);
},
(err, module) => {
...
}
);
好了,本文从_addModuleChain的回调开始说起。
2. 完成make
_addModuleChain
完成之后,模块就都加载完了,
分别调用了各种相应的loader把文件都加载到了内存中。
由于调用链路上,
hooks.make调用了compilation.addEntry,再调用了this._addModuleChain,
因此,this._addModuleChain完成之后,实际上导致hooks.make结束了。
于是,流程走到了Compiler.js 第537行
this.hooks.make.callAsync(compilation, err => {
log('end: make');
if (err) return callback(err);
3. compilation.seal
完成make之后,webpack会进行下一个比较重要的环节,就是compilatin.seal
这个过程中,它会生成所有的代码(只是还未写入文件中)。
compilation.seal在这里实现,Compilation.js 第1159行
seal的执行过程中,调用了一堆hooks,例如,
this.hooks.reviveModules.call(this.modules, this.records);
this.hooks.optimizeModuleOrder.call(this.modules);
this.hooks.advancedOptimizeModuleOrder.call(this.modules);
this.hooks.beforeModuleIds.call(this.modules);
this.hooks.moduleIds.call(this.modules);
this.applyModuleIds();
this.hooks.optimizeModuleIds.call(this.modules);
this.hooks.afterOptimizeModuleIds.call(this.modules);
4. createChunkAssets
其中生成代码的环节位于Complation.js 第1270行
this.createChunkAssets();
它的实现位于Compilation.js 第2313行
并在Compilation.js 第2393行,将生成的代码,保存到了compilation.assets对象中。
this.assets[file] = source;
5. 代码是如何生成的
下面我们来跟踪source
,看看它是如何生成的,
对代码进行调试后,我们发现,source
来源在这里 Compilation.js 第2369行,
source = fileManifest.render();
这个render
的实现在JavascriptModulesPlugin.js中第64行,
result.push({
render: () =>
compilation.mainTemplate.render(
hash,
chunk,
moduleTemplates.javascript,
dependencyTemplates
),
filenameTemplate,
pathOptions: {
noChunkHash: !useChunkHash,
contentHashType: "javascript",
chunk
},
identifier: `chunk${chunk.id}`,
hash: useChunkHash ? chunk.hash : fullHash
});
6. fileManifest.render调用compilation.mainTemplate.render来生成代码
其中调用了,hooks.render生成代码,
let source = this.hooks.render.call(
new OriginalSource(
Template.prefix(buf, " \t") + "\n",
"webpack/bootstrap"
),
chunk,
hash,
moduleTemplate,
dependencyTemplates
);
注:
hooks.render的实现在MainTemplate.js 第148行,
其中又调用了hooks.modules,
而hooks.modules的实现在JavascriptModulesPlugin.js 第86行,
其中接着调用了,Template.renderChunkModules。
7. compilation.assets创建完成
compilation.assets创建完成之后,在经历seal中一系列hooks,
就完成了seal操作。
seal完成后,compile就完成了,会导致this.compile的回调被调用。
compilation.seal(err => {
if (err) return callback(err);
this.hooks.afterCompile.callAsync(compilation, err => {
if (err) return callback(err);
return callback(null, compilation);
});
});
8. onCompiled
this.compile是在Compiler.js 第268行被调用的。
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
this.hooks.run.callAsync(this, err => {
if (err) return finalCallback(err);
this.readRecords(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
});
而onCompiled函数中生成了文件。
9. runAsChild
值得注意是,compile方法并不只是由Compiler.js 第268行调用。
还有可能在其他webpack插件中,通过compiler.runAsChild来调用。
例如,extract-text-webpack-plugin,会调用runAsChild,
childCompiler.runAsChild((err, entries, compilation) => {
这会导致包含extract-text-webpack-plugin的webpack项目调试起来非常困难。
因为插件中也进行了this.compile调用。
网友评论