.17-淺析webpack源碼之compile-入口函數run

来源:https://www.cnblogs.com/QH-Jimmy/archive/2017/12/25/8109237.html
-Advertisement-
Play Games

本節流程如圖: 現在正式進入打包流程,起步方法為run: 為什麼不介紹compiler對象?因為構造函數中並沒有一個初始化的方法,只是普通的變數聲明,沒啥好講的。 在run方法中,首先是調用了tapable的applyPluginsAsync執行了before-run事件流,該事件流的定義地點如下: ...


  本節流程如圖:

  現在正式進入打包流程,起步方法為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事件流,該事件流的定義地點如下:

// NodeEnvironmentPlugin
compiler.plugin("before-run", (compiler, callback) => {
    if (compiler.inputFileSystem === inputFileSystem)
        inputFileSystem.purge();
    callback();
});

  在對compiler對象的文件系統方法的掛載插件中,註入了before-run這個事件流,這裡首先看一下applyPluginsAsync(做了小幅度的修改以適應webpack源碼):

// tapable
Tapable.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方法之前見過,這裡複習下內容:

// NodeEnvironmentPlugin
compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000);

// CachedInputFileSystem
CachedInputFileSystem.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 => Storage
Storage.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事件流,最後調用了回調函數。

 

  從下一節開始詳細講解每一步的流程(不懂的地方肯定會跳過啦)。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • //1var name='world';(function(){ if(typeof name 'undefined'){ var name='jack'; console.log('Goodbye'+name); }else{ console.log('Hello'+name); }})();// ...
  • 轉自腳本之家: 本篇文章主要介紹了angular中實現li或者某個元素點擊變色的兩種方法,非常具有實用價值,需要的朋友可以參考下 本文介紹了angular中實現li或者某個元素點擊變色的兩種方法,分享給大家,希望對大家有幫助 先說一種最直接了當的不需要js控制。 方法一:直接在用ng-class就可 ...
  • 本來以為 Vue 的編譯器模塊比較好欺負,結果發現並沒有那麼簡單。每一種語法指令都要考慮到,處理起來相當複雜。上篇已經生成了 AST,本篇依然對 Vue 源碼做簡化處理,探究 Vue 是如果根據 AST 生成所需要的 render 函數的。 ...
  • 代碼: 方案一: div絕對定位水平垂直居中【margin:auto實現絕對定位元素的居中】, 相容性:,IE7及之前版本不支持 .father{ width:400px; height:400px; background: red; position:relative; /* 或者position ...
  • 最近剛剛用vue寫了個公司項目,使用vue-cli構建的,算是中大型項目吧,然後這裡想記錄並且分享一下其中的知識點,希望對大家有幫助,後期會逐漸分享;話不多說,直接上代碼!! app.vue main.js router文件夾裡面的index.js home.vue pagevue.vue next ...
  • 一、前端MVC概要 1.1、庫與框架的區別 框架是一個軟體的半成品,在全局範圍內給了大的約束。庫是工具,在單點上給我們提供功能。框架是依賴庫的。Vue是框架而jQuery則是庫。 1.2、AMD與CMD 在傳統的非模塊化JavaScript開發中有許多問題:命名衝突、文件依賴、跨環境共用模塊、性能優 ...
  • 閑話不多說,開篇擼代碼,你可以會看到類似如下的結構: See the Pen react props by 糊一笑 (@rynxiao) on CodePen. 當一個組件嵌套了若幹層子組件時,而想要在特定的組件中取得父組件的屬性,就不得不將 一層一層地往下傳,我這裡只是簡單的列舉了3個子組件,而當 ...
  • 1、示例代碼 (註:寫到vue單文件中了) 2、說明 (1)需要transition 標簽包裹。 (2)6個class狀態 (3)效果: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...