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犒赏自己