webpack.js小尾巴
const webpack = (options, callback) => {
//...
if (callback) {
if (typeof callback !== "function") {
throw new Error("Invalid argument: callback");
}
if (
options.watch === true ||
(Array.isArray(options) && options.some(o => o.watch))
) {
const watchOptions = Array.isArray(options)
? options.map(o => o.watchOptions || {})
: options.watchOptions || {};
return compiler.watch(watchOptions, callback);
}
compiler.run(callback);
}
return compiler;
}
最終返回了compiler
exports.version = version;
// ...屬性掛載,把引入的函式模組全部暴露出來
webpack.WebpackOptionsDefaulter = WebpackOptionsDefaulter;
webpack.WebpackOptionsApply = WebpackOptionsApply;
webpack.Compiler = Compiler;
webpack.MultiCompiler = MultiCompiler;
webpack.NodeEnvironmentPlugin = NodeEnvironmentPlugin;
// @ts-ignore Global @this directive is not supported
webpack.validate = validateSchema.bind(this, webpackOptionsSchema);
webpack.validateSchema = validateSchema;
webpack.WebpackOptionsValidationError = WebpackOptionsValidationError;
下面暴露了一些外掛
const exportPlugins = (obj, mappings) => {
for (const name of Object.keys(mappings)) {
Object.defineProperty(obj, name, {
configurable: false,
enumerable: true,
get: mappings[name]
});
}
};
exportPlugins(exports, {
AutomaticPrefetchPlugin: () => require("./AutomaticPrefetchPlugin"),
BannerPlugin: () => require("./BannerPlugin"),
CachePlugin: () => require("./CachePlugin")}
)
再通俗一點的解釋:
比如當你api.AutomaticPrefetchPlugin你能
呼叫AutomaticPrefetchPlugin檔案下的方法
這個和上面的不同在於上面的是掛在webpack函式物件上的
Compiler.js正題
要想理解Compiler.js
必須要理解tapable
再寫一遍 git地址
我們先簡單的理解它為一個通過tap 註冊外掛
call是run外掛的事件流,裡面還有一些非同步的操作
Compiler整理如下
class Compiler extends Tapable {
constructor(context) {
super();
this.hooks = {
//羅列一些出現頻率比較高的
shouldEmit: new SyncBailHook(["compilation"]),
done: new AsyncSeriesHook(["stats"]),
beforeRun: new AsyncSeriesHook(["compiler"]),
run: new AsyncSeriesHook(["compiler"]),
emit: new AsyncSeriesHook(["compilation"]),
afterEmit: new AsyncSeriesHook(["compilation"]),
thisCompilation: new SyncHook(["compilation", "params"]),
compilation: new SyncHook(["compilation", "params"]),
beforeCompile: new AsyncSeriesHook(["params"]),
compile: new SyncHook(["params"]),
make: new AsyncParallelHook(["compilation"]),
afterCompile: new AsyncSeriesHook(["compilation"]),
watchRun: new AsyncSeriesHook(["compiler"]),
//...
}
// 新增事件流
this._pluginCompat.tap("Compiler", options => {
switch (options.name) {
case "additional-pass":
case "before-run":
case "run":
case "emit":
case "after-emit":
case "before-compile":
case "make":
case "after-compile":
case "watch-run":
options.async = true;
break;
}
});
}
watch(){
//...
}
run(callback) {
//...
}
// 清除輸入檔案系統
purgeInputFileSystem() {
if (this.inputFileSystem && this.inputFileSystem.purge) {
this.inputFileSystem.purge();
}
}
emitAssets(compilation, callback) {
//...
}
createChildCompiler(
compilation,
compilerName,
compilerIndex,
outputOptions,
plugins
) {
//...
}
//...
compile(callback){
//...
}
}
和tapable用法一個模子裡刻出來的,我們仔細的研究run函式
compiler.js是webpack 的核心
run(callback) {
//如果正在running,返回報錯處理
if (this.running) return callback(new ConcurrentCompilationError());
//running呼叫finalCallback
const finalCallback = (err, stats) => {
this.running = false;
if (callback !== undefined) return callback(err, stats);
};
//記錄初始化running時間
const startTime = Date.now();
//設定running標誌,防止多次running
this.running = true;
//正在編譯
const onCompiled = (err, compilation) => {
//如果報錯,編譯結束
if (err) return finalCallback(err);
//如果沒有編譯完成
if (this.hooks.shouldEmit.call(compilation) === false) {
// Stats模組有1400行,
// compiler.js是webpack 的核心,new Stats(compilation)是compiler.js的核心
const stats = new Stats(compilation);
// stats物件掛載startTime,endTime
stats.startTime = startTime;
stats.endTime = Date.now();
// 非同步呼叫完成事件流,running結束
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
return;
}
// 呼叫emitAsset方法,emitAsset主要負責寫入檔案輸出檔案,不影響我們先看編譯
this.emitAssets(compilation, err => {
// 類似同上, 如果報錯,編譯結束
if (err) return finalCallback(err);
// 如果需要額外的編譯,上次的沒編譯完成嗎?
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
//再來一遍new Stats
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
//繼續非同步呼叫時間流
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
// 這次多了一個時間流,呼叫額外編譯,告知編譯終於編完了
this.hooks.additionalPass.callAsync(err => {
if (err) return finalCallback(err);
//呼叫compile,把onCompiled的返回值傳入compile函式,onCompiled的返回值也就是new Stats的物件
this.compile(onCompiled);
});
});
return;
}
// 如果都不走上面的分支,即編譯完成,不需要額外的編譯
// 呼叫emitRecords方法,主要用來記錄輸出的
this.emitRecords(err => {
if (err) return finalCallback(err);
// 同樣new Stats
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
// 非同步呼叫完成事件
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
});
//最終總結,無論走那個分支都是 new Stats(compilation) 返回stats的回撥函式,按照目前的流程走的是最後一個分支,呼叫 this.emitRecords
});
};
// 呼叫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);
//把onCompiled函式傳入,呼叫compile
this.compile(onCompiled);
});
});
});
}
new Stats(compilation)是compiler.js的核心
compilation和Stats分別對應兩個模組
compilation.js 2500行,Stats.js 1400行
接下來我們看一下compile函式
newCompilationParams() {
const params = {
// 普通模組工廠
normalModuleFactory: this.createNormalModuleFactory(),
// 上下文模組工廠
contextModuleFactory: this.createContextModuleFactory(),
// 編譯依賴
compilationDependencies: new Set()
};
return params;
}
compile(callback) {
// params值如下
const params = this.newCompilationParams();
// 非同步呼叫beforeCompile鉤子
this.hooks.beforeCompile.callAsync(params, err => {
if (err) return callback(err);
// 呼叫compile鉤子
this.hooks.compile.call(params);
// 終於出現了compilation,之前一直沒有講了compilation是什麼,newCompilation我們看如下函式
const compilation = this.newCompilation(params);
this.hooks.make.callAsync(compilation, err => {
if (err) return callback(err);
compilation.finish();
compilation.seal(err => {
if (err) return callback(err);
// 非同步呼叫afterCompile,返回回撥函式
this.hooks.afterCompile.callAsync(compilation, err => {
if (err) return callback(err);
return callback(null, compilation);
});
});
});
});
}
newCompilation(params) {
// compilation 是 this.createCompilation(),繼續往下
const compilation = this.createCompilation();
//給compilation物件掛載屬性
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;
}
createCompilation() {
// 原來compilation 是 newCompilation而來,Compilation一共2500行,堪稱整個compiler.js的核心
return new Compilation(this);
}
params如下
{ normalModuleFactory:
NormalModuleFactory {
_pluginCompat:
SyncBailHook {
// key是tapable 方法名
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined },
hooks:
{ resolver: [SyncWaterfallHook],
factory: [SyncWaterfallHook],
beforeResolve: [AsyncSeriesWaterfallHook],
afterResolve: [AsyncSeriesWaterfallHook],
createModule: [SyncBailHook],
module: [SyncWaterfallHook],
createParser: [HookMap],
parser: [HookMap],
createGenerator: [HookMap],
generator: [HookMap] },
resolverFactory:
ResolverFactory {
_pluginCompat: [SyncBailHook],
hooks: [Object],
cache1: [WeakMap],
cache2: Map {} },
ruleSet: RuleSet { references: {}, rules: [Array] },
cachePredicate: [Function: bound Boolean],
//檔案路徑
context: `/Users/orion/Desktop/react-beauty-highcharts`,
parserCache: {},
generatorCache: {} },
contextModuleFactory:
ContextModuleFactory {
_pluginCompat:
SyncBailHook {
_args: [Array],
taps: [Array],
interceptors: [],
call: [Function: lazyCompileHook],
promise: [Function: lazyCompileHook],
callAsync: [Function: lazyCompileHook],
_x: undefined },
hooks:
{ beforeResolve: [AsyncSeriesWaterfallHook],
afterResolve: [AsyncSeriesWaterfallHook],
contextModuleFiles: [SyncWaterfallHook],
alternatives: [AsyncSeriesWaterfallHook] },
resolverFactory:
ResolverFactory {
_pluginCompat: [SyncBailHook],
hooks: [Object],
cache1: [WeakMap],
cache2: Map {} } },
compilationDependencies: Set {} }
終極總結
- Compiler建構函式 ->
- run方法 ->
- this.compile(onCompiled) ->
- this.compile()執行有了compilation ->
- onCompiled執行 const stats = new Stats(compilation)
- 最後返回 finalCallback(null, stats);
this.compile(onCompiled) 是個高階函式,可以簡單的理解為引數是函式,並且返回一個函式
撒花? ??我要買瓶skii犒賞自己