Webpack是目前基于React和Redux開發的應用的主要打包工具。我想使用Angular 2或其他框架開發的應用也有很多在使用Webpack。
本節流程如圖:
現在正式進入打包流程,起步方法為run:
Compiler.prototype.run = (callback) => { const startTime = Date.now(); const onCompiled = (err, compilation) => { /**/ }; this.applyPluginsAsync("before-run", this, err => { if (err) return callback(err); this.applyPluginsAsync("run", this, err => { if (err) return callback(err); this.readRecords(err => { if (err) return callback(err); this.compile(onCompiled); }); }); });}
為什么不介紹compiler對象?因為構造函數中并沒有一個初始化的方法,只是普通的變量聲明,沒啥好講的。
在run方法中,首先是調用了tapable的applyPluginsAsync執行了before-run事件流,該事件流的定義地點如下:
// NodeEnvironmentPlugincompiler.plugin("before-run", (compiler, callback) => { if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge(); callback();});
在對compiler對象的文件系統方法的掛載插件中,注入了before-run這個事件流,這里首先看一下applyPluginsAsync(做了小幅度的修改以適應webpack源碼):
// tapableTapable.prototype.applyPluginsAsync = (name, ...args, callback) => { var plugins = this._plugins[name]; if (!plugins || plugins.length === 0) return callback(); var i = 0; var _this = this; // args為[args,next函數] args.push(copyProperties(callback, function next(err) { // 事件流出錯或者全部執行完后調用回調函數 if (err) return callback(err); i++; if (i >= plugins.length) { return callback(); } // 執行下一個事件 plugins[i].apply(_this, args); })); // 執行第一個事件 plugins[0].apply(this, args);};
當時在第八節沒有講這個系列的事件流觸發方式,這里簡單說下:
1、copyProperties用于對象屬性的拷貝,類似于Object.assign,然而在這里傳入的是兩個函數,一點用都沒有?。。。?!(當時沒寫講解就是因為一直卡在這個對象拷貝方法在這里有什么毛用)
2、在webpack中,args為一個this,指向compiler的上下文
3、注入該事件流的事件必須要執行callback方法(如上例),此時執行的并不是外部的callback,而是next函數
4、有兩種情況下會執行外部callback,中途出錯或者所有事件流執行完畢
這樣就很明白了,注入before-run中的函數形參的意義如下:
// before-run// compiler => this// callback => next(compiler, callback) => { if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge(); callback();}
由于before-run中只有一個事件,所以在調用內部callback的next方法后,會由于i大于事件長度而直接調用外部callback。
這里的purge方法之前見過,這里復習下內容:
// NodeEnvironmentPlugincompiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000);// CachedInputFileSystemCachedInputFileSystem.prototype.purge = function(what) { this._statStorage.purge(what); this._readdirStorage.purge(what); this._readFileStorage.purge(what); this._readlinkStorage.purge(what); this._readJsonStorage.purge(what);};// CachedInputFileSystem => StorageStorage.prototype.purge = function(what) { if (!what) { this.count = 0; clearInterval(this.interval); this.nextTick = null; this.data.clear(); this.levels.forEach(function(level) { level.clear(); }); } else if (typeof what === "string") { /**/ } else { /**/ }};
一句話概括就是:清除所有打包中緩存的數據。
由于假設是第一次,所以這里并沒有什么實際操作,接著調用外部callback,用同樣的方式觸發了run事件流。
run事件流也只有一個方法,來源于CachePlugin插件:
Compiler.plugin("run", (compiler, callback) => { // 這個屬性我暫時也不知道是啥 反正直接callback了 if (!compiler._lastCompilationFileDependencies) return callback(); const fs = compiler.inputFileSystem; const fileTs = compiler.fileTimestamps = {}; asyncLib.forEach(compiler._lastCompilationFileDependencies, (file, callback) => { // ... }, err => { // ... });});
在第一次觸發run事件流時,那個屬性是undefined,所以會直接跳過,因為我是邊看源碼邊解析,所以也不知道是啥,哈哈。
接下來下一個callback是這個:
this.readRecords(err => { if (err) return callback(err); this.compile(onCompiled);});
這是另一個原型方法,源碼如下:
Compiler.prototype.readRecords = (callback) => { // 這個屬性也沒有 if (!this.recordsInputPath) { this.records = {}; return callback(); } this.inputFileSystem.stat(this.recordsInputPath, err => { // ... });}
這里第一次也會跳過并直接callback,看源碼大概是傳入一個路徑并讀取里面的文件信息緩存到records中。
這下連跳兩步,直接進入原型方法compile中,預覽一下這個函數:
Compiler.prototype.compile = (callback) => { const params = this.newCompilationParams(); // 依次觸發事件流 this.applyPluginsAsync("before-compile", params, err => { if (err) return callback(err); this.applyPlugins("compile", params); const compilation = this.newCompilation(params); this.applyPluginsParallel("make", compilation, err => { if (err) return callback(err); compilation.finish(); compilation.seal(err => { if (err) return callback(err); this.applyPluginsAsync("after-compile", compilation, err => { if (err) return callback(err); return callback(null, compilation); }); }); }); });}
編譯打包的核心流程已經一覽無遺,方法中依次觸發了before-compile、compile、make、after-compile事件流,最后調用了回調函數。
總結
以上所述是小編給大家介紹的webpack源碼之compile流程-入口函數run,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答