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