赞
踩
try { const { compiler, watch, watchOptions } = create(); if (watch) { compiler.watch(watchOptions, callback); } else { compiler.run((err, stats) => { compiler.close(err2 => { callback( err || err2, /** @type {options extends WebpackOptions ? Stats : MultiStats} */ (stats) ); }); }); } return compiler; } catch (err) { process.nextTick(() => callback(/** @type {Error} */ (err))); return null; }
来自于Compiler.prototype.run方法webpack/lib/Compiler.js
// 代码精简过 class Compiler { constructor () {} run(callback) { // 判断compiler.running状态,防止重复执行相同的编译任务,若处于执行状态,则抛出异常终止本次调用 if (this.running) { return callback(new ConcurrentCompilationError()); } // finalCallback是编译流程的最终回调,持久化缓存的写入信号就是在这里释放的。 const finalCallback = (err, stats) => {}; // 设置compiler.running方法为true this.running = true; // 声明onCompiled内部方法,用于处理编译过程中的事件回调,根据编译的状态和钩子函数的返回值执行不同的操作 const onCompiled = (err, compilation) => { }; // 声明内部的run方法。 const run = () => {}; // 判断当前的空闲状态。该标识符在持久化缓存写入的时候为true,根据状态不同有不同的处理,true则需要等待缓存处理结束的会调里调用run方法启动编译。this.idle为false,则直接调用run方法 if (this.idle) { this.cache.endIdle(err => { this.idle = false; run(); }); } else { run(); } } }
const run = () => {
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);
});
});
});
};
触发compiler.hooks.beforeRun钩子,传入compiler实例和回调。订阅该钩子的插件有
class NodeEnvironmentPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {
if (compiler.inputFileSystem === inputFileSystem) {
compiler.fsStartTime = Date.now();
inputFileSystem.purge(); // 出清文件系统
}
});
}
}
如果当前的文件系统是给定的inputFileSystem,则记录当前的时间,重新编译,此前的文件系统中的内容没用了
2. ProgressPlugin
class ProgressPlugin {
// 简化后的
constructor () {
apply(compiler) {
interceptHook(compiler.hooks.beforeRun, 0.01, "setup", "before run")
}
}
}
输出webpack构建进度的
在compiler.hooks.beforeRun的回调中触发hooks.run钩子,webpack内部暂时无法插件订阅该钩子
//代码简化 class Compiler { readRecords(callback) { if (this.hooks.readRecords.isUsed()) { if (this.recordsInputPath) { asyncLib.parallel([ cb => this.hooks.readRecords.callAsync(cb), this._readRecords.bind(this) ]); } else { this.records = {}; this.hooks.readRecords.callAsync(callback); } } else { if (this.recordsInputPath) { this._readRecords(callback); } else { this.records = {}; callback(); } } } }
首先判断是否注册了compiler.hooks.readRecords钩子,有则判断是否有recordsInputPath配置,有则触发this.hooks.readRecords,会触发使用records的相关插件执行。然后触发this._readRecords.bind(this)
this._readRecords
class Compielr { //.... _readRecords(callback) { //路径不存在 if (!this.recordsInputPath) { this.records = {}; return callback(); } // 路径存在 this.inputFileSystem.stat(this.recordsInputPath, err => { this.inputFileSystem.readFile(this.recordsInputPath, (err, content) => { this.records = parseJson(content.toString("utf-8")); return callback(); }); }); } }
上面的函数处理的是beforeCompile到afterCompile钩子,onCompiled函数则处理后续的编译产物和records的写入工作。
const onCompiled = (err, compilation) => { // err对象,编译失败,调用finalCallback这个函数终止编译 if (err) return finalCallback(err); // 触发this.hooks.shouldEmit.call这个钩子。 // shouldEmit标识是否输出编译产物,如果不想让产物输出,可以订阅这个钩子,并在这个钩子最后返回false,这样可以防止产物写入本地文件系统。 if (this.hooks.shouldEmit.call(compilation) === false) { // 阻止文件写入 compilation.startTime = startTime; compilation.endTime = Date.now(); const stats = new Stats(compilation); this.hooks.done.callAsync(stats, err => { if (err) return finalCallback(err); return finalCallback(null, stats); }); return; } process.nextTick(() => { // 写入文件 this.emitAssets(compilation, err => { if (compilation.hooks.needAdditionalPass.call()) { // 暂时忽略 } // 完成写入工作 this.emitRecords(err => { compilation.startTime = startTime; compilation.endTime = Date.now(); const stats = new Stats(compilation); this.hooks.done.callAsync(stats, err => { this.cache.storeBuildDependencies( compilation.buildDependencies, err => { // records完成后调用finalCallback函数完成最终的收尾工作 return finalCallback(null, stats); } ); }); }); }); }); };
finalCallback函数
用于处理compiler.run方法的收尾工作
const finalCallback = (err, stats) => {
// idle是处理持久化缓存的标识
this.idle = true;
this.cache.beginIdle();
this.idle = true;
// 关闭本编译器
this.running = false;
if (err) {
this.hooks.failed.call(err);
}
// callback是webpack-cli里面启动编译器传入的,这里算是webpack编译器和webpack-cli在通信。
if (callback !== undefined) callback(err, stats);
// 触发compiler.hooks.afterDone钩子,告知订阅插件做收编译工作的总结。
this.hooks.afterDone.call(stats);
};
调用compiler.cache.benginIdle()方法, 标识编译器空闲,可以进行缓存写入工作。后续在IdleFileCachePlugin和PackFileCacheStrategy完成
webpack的运行是一个串行的过程
合并shell和配置文件的参数形成实例化complier对象 => 加载插件 => 处理入口
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。