01 => 绑定entryOption钩子
- 内部主要实例化了SingleEntryPlugin类,并调用内部的apply方法
- 实例化之后有一行代码:compiler.hooks.entryOption.call(options.context, options.entry),会立即对entryOption钩子进行调用
```javascript
// 此处我们传入的是./src/index.js,为字符串类型
const itemToPlugin = (context, item, name) => {
if (Array.isArray(item)) {
} return new SingleEntryPlugin(context, item, name); };return new MultiEntryPlugin(context, item, name);
module.exports = class EntryOptionPlugin { apply(compiler) { compiler.hooks.entryOption.tap(“EntryOptionPlugin”, (context, entry) => { if (typeof entry === “string” || Array.isArray(entry)) { itemToPlugin(context, entry, “main”).apply(compiler); } else { … } return true; }); } }
<a name="p1Xzz"></a>### 02 => SingleEntryPlugin类内部主要绑定了make钩子,compilation.addEntry是编译的入口。直到此处,compiler准备工作就完成了。```javascriptclass SingleEntryPlugin {// 将传入的属性绑定到this上constructor(context, entry, name) {this.context = context;this.entry = entry;this.name = name;}// 主要绑定了make钩子,此处是编译的入口apply(compiler) {compiler.hooks.make.tapAsync("SingleEntryPlugin",(compilation, callback) => {const { entry, name, context } = this;// dep 可以简单理解为 entry// 即compilation.addEntry(context, entry, name, callback);const dep = SingleEntryPlugin.createDependency(entry, name);compilation.addEntry(context, dep, name, callback);});}}
03 => 调用compiler的run方法,开始对源文件编译
- Compiler类在webpack源码01中说讲过,当时主要是进行实例化操作,绑定了一些内部属性
- 当我们调用run方法时,内部会调用一系列Tapable的钩子,如beforeRun、run等等
- 最终执行一行代码:this.hooks.make.callAsync,执行上述绑定的make钩子
class Compiler extends Tapable { // callback => 回调函数 run(callback) { // finalCallback函数是专门处理错误的,此处我们不考虑错误的情况 // 调用beforeRun钩子 this.hooks.beforeRun.callAsync(this, err => { if (err) return finalCallback(err); // 调用run钩子 this.hooks.run.callAsync(this, err => { if (err) return finalCallback(err); this.readRecords(err => { if (err) return finalCallback(err); // 调用compile方法 // 传入onCompiled回调,onCompiled函数作用是将处理好的chunk输出到dist目录 // onCompiled函数后续再讲 this.compile(onCompiled); }); }); }) } // 创建NormalModuleFactory工厂 // NormalModuleFactory类内部就一个create方法,等到之后再说 createNormalModuleFactory() { const normalModuleFactory = new NormalModuleFactory( this.options.context, this.resolverFactory, this.options.module || {} ); // 执行normalModuleFactory钩子 this.hooks.normalModuleFactory.call(normalModuleFactory); // 返回实例化后的NormalModuleFactory类 return normalModuleFactory; } // Compilation对象 => 每一次编译的过程都会生成一个Compilation对象 // 实例化Compilation类并设置属性 // 调用thisCompilation和compilation钩子 newCompilation(params) { const compilation = new Compilation(this); compilation.fileTimestamps = this.fileTimestamps; compilation.contextTimestamps = this.contextTimestamps; compilation.name = this.name; compilation.records = this.records; compilation.compilationDependencies = params.compilationDependencies; this.hooks.thisCompilation.call(compilation, params); this.hooks.compilation.call(compilation, params); return compilation; } // 此处主要是normalModuleFactory,因此主要看createNormalModuleFactory函数 newCompilationParams() { const params = { normalModuleFactory: this.createNormalModuleFactory(), contextModuleFactory: this.createContextModuleFactory(), compilationDependencies: new Set() }; return params; } compile(callback) { // 可以理解成简单的一个对象,内部有一个create方法 const params = this.newCompilationParams(); // 调用beforeCompile钩子 this.hooks.beforeCompile.callAsync(params, err => { if (err) return callback(err); // 调用compile钩子 this.hooks.compile.call(params); // 创建Compilation类并触发一系列钩子 const compilation = this.newCompilation(params); // 执行02中绑定的make钩子,开始编译 this.hooks.make.callAsync(compilation, err => { if (err) return callback(err); // 编译完成后,处理的函数 compilation.finish(err => { if (err) return callback(err); // 根据已有的module,生成对应的代码 compilation.seal(err => { if (err) return callback(err); // 调用编译完成后的函数 this.hooks.afterCompile.callAsync(compilation, err => { if (err) return callback(err); return callback(null, compilation); }); }) }) }) }) } }
04 => Compilation对象
Compilation和Compiler是webpack核心的两个对象。
- Compiler只会生成一次,记录了完成的webpack环境信息
- Compilation在watch时会生成多次,代表编译过程
// Compilation类也继承了Tapable库 // 设置一些内部属性 class Compilation extends Tapable { constructor(compiler) { super(); this.hooks = { addEntry: new SyncHook(["entry", "name"]), succeedEntry: new SyncHook(["entry", "name", "module"]), finishModules: new AsyncSeriesHook(["modules"]), beforeChunks: new SyncHook([]), afterChunks: new SyncHook(["chunks"]), ... } this.compiler = compiler; this.options = options; this.entries = []; this.chunks = []; this.modules = []; ... } }
05 => 调用compiler.hooks.make,开始编译
调用的是Compilation对象中的addEntry方法
class Compilation extends Tapable {
// context => 上下文对象
// entry => option.entry,传入的入口地址
// name => "main"
// callback => 回调函数
addEntry(context, entry, name, callback) {
// 调用addEntry钩子
this.hooks.addEntry.call(entry, name);
...
// 调用_addModuleChain方法
this._addModuleChain(
context,
entry,
module => {
// this.entries为存入所有入口模块的地址,初始为空数组
this.entries.push(module);
},
(err, module) => {
...
// 调用succeedEntry钩子
this.hooks.succeedEntry.call(entry, name, module);
return callback(null, module);
}
);
}
// context => 上下文对象
// dependency => 依赖项,上述为入口地址
// onModule => 往this.entries添加入口模块地址
// callback => 回调
_addModuleChain(context, dependency, onModule, callback) {
const Dep = /** @type {DepConstructor} */ (dependency.constructor);
// moduleFactory => NormalModuleFactory类
const moduleFactory = this.dependencyFactories.get(Dep);
// 调用NormalModuleFactory类中的create方法
moduleFactory.create(
// 传入的data
{
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
},
// 回调函数,主要工作就是往this.modules添加编译后的模块,以及设置缓存相关
// module => 实例化NormalModules后的对象
(err, module) => {
// 如果有错误信息,直接结束
if (err) {
this.semaphore.release();
return errorAndCallback(new EntryModuleNotFoundError(err));
}
...
// 里面有一句this.modules.push(module)代码
// 往this.modules添加编译后的模块
const addModuleResult = this.addModule(module);
module = addModuleResult.module;
// 添加入口依赖的模块
onModule(module);
dependency.module = module;
...
if (addModuleResult.build) {
// 开始编译模块
// 内部调用module.build()方法
this.buildModule(module, false, null, null, err => {
if (err) {
...
}
...
// build 完成后
afterBuild();
});
}
}
)
}
}
06 => 实例化NormalModule,开始build
主要工作是执行了Tapable的factory钩子,实例化NormalModule类。最终走到了回调,回到了 05中
class NormalModuleFactory extends Tapable {
constructor(context, resolverFactory, options) {
super();
...
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
// 调用resolver钩子
let resolver = this.hooks.resolver.call(null);
resolver(result, (err, data) => {
if (err) return callback(err);
if (!data) return callback();
// 调用afterResolve钩子
this.hooks.afterResolve.callAsync(data, (err, result) => {
let createdModule = this.hooks.createModule.call(result);
if (!createdModule) {
if (!result.request) {
return callback(new Error("Empty dependency (no request)"));
}
// 内部调用了NormalModule类
createdModule = new NormalModule(result);
}
// 调用module钩子
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
})
})
})
}
...
create(data, callback) {
// 判断是否存在缓存,如果存在缓存直接返回
const dependencies = data.dependencies;
const cacheEntry = dependencyCache.get(dependencies[0]);
if (cacheEntry) return callback(null, cacheEntry);
// 获取属性
const context = data.context || this.context;
const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
const request = dependencies[0].request;
// 调用beforeResolve钩子
this.hooks.beforeResolve.callAsync(
{
contextInfo,
resolveOptions,
context,
request,
dependencies
},
(err, result) => {
if (err) return callback(err);
// 调用factory钩子,factory钩子在constructor的时候就注册了
const factory = this.hooks.factory.call(null);
if (!factory) return callback();
factory(result, (err, module) => {
if (err) return callback(err);
// 设置缓存
if (module && this.cachePredicate(module)) {
for (const d of dependencies) {
dependencyCache.set(d, module);
}
}
// 此时回到了 05中
callback(null, module);
});
}
)
}
}
