.3-淺析webpack源碼之預編譯總覽


  寫在前面:

  本來一開始想沿用之前vue源碼的標題:webpack源碼之***,但是這個工具比較巨大,所以為防止有人覺得我裝逼跑來噴我(或者隨時鴿),加上淺析二字,以示慫。

  既然是淺析,那么案例就不必太復雜,所以繼續按照之前vue源碼,以最簡單形式進行源碼分析,如下:

  配置文件config.js:

module.exports={
    entry:'./entry.js',
    output:{
        filename:'output'
    }
}

  入口文件entry.js:

console.log('entry');

  執行命令為:

node webpack --config config.js

  不摸魚了,開始正式進入源碼,慣例上圖: 

  

 

  之前簡單講解了webpack.cmd的內容,可以發現在正常進行webpack打包之前,會調用bin文件夾的webpack.js文件,這一節就看一下這個文件做了什么,經過簡單整理,源碼如下:

var path = require("path");

// 優先使用當前路徑版本的webpack.js
try {
    var localWebpack = require.resolve(path.join(process.cwd(), "node_modules", "webpack", "bin", "webpack.js"));
    if(__filename !== localWebpack) {
        return require(localWebpack);
    }
} catch(e) {}

// 引入yargs框架
var yargs = require("yargs")
    .usage("字符串...");

// 配置
require("./config-yargs")(yargs);

var DISPLAY_GROUP = "Stats options:";
var BASIC_GROUP = "Basic options:";

yargs.options({
    "json": {
        type: "boolean",
        alias: "j",
        describe: "Prints the result as JSON."
    },
    // ...
    // 各種配置...
});

// 解析命令行參數並執行回調函數
// 正常情況下err為null output為空字符
yargs.parse(process.argv.slice(2), (err, argv, output) => {
    // 報錯信息
    if(err && output) {
        // ...
    }

    // 輸出幫助或版本信息
    if(output) {
        // ...
    }

    // 這里對配置文件進行轉換與合法性檢測
    var options = require("./convert-argv")(yargs, argv);

    // argv的參數二次處理
    // 暫時無視
    function ifArg(name, fn, init) {
        // ...
    }

    // 解析配置文件
    function processOptions(options) {
        // 當前配置文件是一個Promise時
        if(typeof options.then === "function") {
            options.then(processOptions).catch(function(err) {
                console.error(err.stack || err);
                process.exit(1); // eslint-disable-line
            });
            return;
        }

        // 大量的ifArg(...)

        // 獲取webpack主函數 
        var webpack = require("../lib/webpack.js");

        Error.stackTraceLimit = 30;
        var lastHash = null;
        var compiler;
        try {
            // 編譯
            compiler = webpack(options);
        } catch(err) {
            // error
        }

        // ...編譯后回調 暫時不管
    }

    // 執行上面的函數
    processOptions(options);
});

  源碼說長也不長,可以分為五大塊:

1、引入yargs框架並進行配置

2、使用yargs解析命令並調用回調函數

3、進行配置文件參數轉換與合法性檢測

4、引入webpack主函數進行編譯

5、編譯完成后調用對應的回調函數

  其中yargs框架是一個命令行框架,具體內容請自行查閱,這里不會做更多介紹,因此源碼內容會按順序跳着講。

 

  首先是一個小try/catch語句:

// Local version replace global one
try {
    var localWebpack = require.resolve(path.join(process.cwd(), "node_modules", "webpack", "bin", "webpack.js"));
    if(__filename !== localWebpack) {
        return require(localWebpack);
    }
} catch(e) {}

  其實注釋已經講明了這個代碼塊的作用,這里稍微詳細再說幾點。

  process是node中的一個全局對象,而process.cwd()方法可以獲取到當前進程的絕對路徑,測試如圖:

  所以第一行賦值語句會嘗試獲取./node_modules/webpack/bin/webpack,js文件並進行調用,將結果賦給localWebpack。

  而__filename是當前指令文件的詳細絕對路徑,在默認的node webpack *指令中,一般情況我們都會配置環境變量,所以這個webpack指向全局的webpack,而__filename也是全局的webpack.js路徑。

  因此,if判斷就是優先使用當前路徑的webpack.js,如果不存在,就使用全局的webpack來進行編譯解析。

  

  接下來的幾塊內容全是yargs的配置,這里就不細看了,直接進入yargs.parse函數:

yargs.parse(process.argv.slice(2), (err, argv, output) => {
    // ...
})

  前面提到過process是node的全局對象,argv是它的一個屬性,主要處理命令參數,可以簡單打印看一下內容:

  在什么也不做的時候,會返回一個數組,其中第一個元素默認為node.exe路徑。

  這里創建一個JS專門打印process.argv:

// entry.js
console.log(process.argv);

  然后執行下列命令:

node entry 1 2 3

  結果如圖:

  可以看到,傳入命令的參數是從第三個開始依次展開,所以yargs.parse的第一個參數排除了argv數組的前兩個,只傳命令參數。

  yargs的parse方法會負責收集並管理參數,在執行成功后會調用后面的函數,回調函數有三個參數,分別為:

1、err => 解析過程中的錯誤信息

2、argv => 參數管理對象

3、output => 輸出到終端的文本

  如果正常解析,err為null,而output為空字符。

  這里可以觀察一下argv的內容,在源碼中打印結果如圖:

  這只是一部分屬性的截圖,對象中的第一個_保存了傳進來的參數,由於直接運行的node webpack --config *,后面沒有帶參數,所以這里是個空數組。

  而其余大量的屬性都是在之前配置中添加的,比如說前面的4個:help、h、version、v,來自於config-yarg.js文件,源碼如下:

module.exports = function(yargs) {
    yargs
        .help("help")
        .alias("help", "h")
        .version()
        .alias("version", "v")
        .options(
            // 更多配置...
        )
    }

  這個配置不是關心的重點,暫時沒必要去看。

 

  接下來是convert-argv函數,負責對配置對象轉換並返回:

    var options = require("./convert-argv")(yargs, argv);

  這個函數有點長,下一節再來講解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM