【webpack】- 4、webpack編譯結果分析

1、分析編譯結果的目的 ?

對webpack的編譯結果的認識對分析編譯過程有幫助,理解了編譯過程對後面使用webpack的加載器和插件的理解有幫助;

2、自己嘗試手寫dist/main.js

自己嘗試手寫dist/main.js
webpack的作用:根據入口文件./src/index.js 分析文件的依賴關係,然後把依賴的模塊合併成一個文件

my-main.js :

//合併兩個模塊
//  ./src/a.js
//  ./src/index.js

(function (modules) {
    var moduleExports = {}; //用於緩存模塊的導出結果

    //require函數相當於是運行一個模塊,得到模塊導出結果
    function __webpack_require(moduleId) { //moduleId就是模塊的路徑
        if (moduleExports[moduleId]) {
            //檢查是否有緩存
            return moduleExports[moduleId];
        }

        var func = modules[moduleId]; //得到該模塊對應的函數
        var module = {
            exports: {}
        }
        func(module, module.exports, __webpack_require); //運行模塊
        var result = module.exports; //得到模塊導出的結果
        moduleExports[moduleId] = result; //緩存起來
        return result;
    }

    //執行入口模塊
    return __webpack_require("./src/index.js"); //require函數相當於是運行一個模塊,得到模塊導出結果
})({ //該對象保存了所有的模塊,以及模塊對應的代碼
    "./src/a.js": function (module, exports) {
        eval("console.log(\"module a\")\nmodule.exports = \"a\";\n //# sourceURL=webpack:///./src/a.js")
    },
    "./src/index.js": function (module, exports, __webpack_require) {
        eval("console.log(\"index module\")\nvar a = __webpack_require(\"./src/a.js\")\na.abc();\nconsole.log(a)\n //# sourceURL=webpack:///./src/index.js")
      
    }
});

3、手寫思路

合併./src/index.js./src/a.js兩個模塊,合併的結果裏就是原始的js代碼,不存在任何模塊化的內容
---------------step1-----------------------------------------

1、避免全局變量污染,避免模塊化代碼(module,exports,require)找不到

問題:

  1. 合併前的模塊化代碼(module,exports,require)怎麼辦?—傳參
  2. 模塊化的模塊裏不會有 全局變量污染,合併後怎麼確保也不會污染全局變量? --模塊裏的內容放在函數裏

方案
我們可以如下,寫成對象的模式, 定義一個變量modules 是一個對象該對象保存了所有的模塊,以及模塊對應的代碼

var modules = {   
    "./src/a.js ": function(module,exports,require){
        console.log('a 模塊');
        module.exports = 'a';
    },     
    "./src/index.js ": function(module,exports,require){
    	// var a = require('./a');
        var a = require('./src/a.js');  //用統一的路徑書寫方式,require('./src/a.js)的時候,就找到對應的函數去執行
        console.log(a);
        
        console.log('index模塊');
    },   
}
  • 模塊的路徑 作爲對象裏的屬性名,因爲每個模塊的路徑是唯一的,所以通過這個唯一的屬性名就可以找到一個唯一的模塊
  • 對象的屬性值可以寫成一個函數,把模塊裏的代碼放到函數環境裏,
  • 在函數裏用到了module,在普通的js裏是沒有module的,我們可以使用函數的參數 給傳進來(commonjs裏還可能用到exports.a =1;我們把exports 也傳進來)

這樣每一個模塊裏面的代碼就沒有污染全局變量!!!

總結:
因爲每個模塊對應一個唯一的路徑,所以我們把路徑用作屬性名屬性值是函數(模塊裏的代碼放在函數裏)
./src/index.js 和 ./src/a.js 這兩個模塊,運行一個模塊,就相當於運行一個函數,就是執行函數裏的模塊代碼

---------------step2-----------------------------------------

2、對象modules交給立即執行函數來處理

問題3:那對象modules交給誰來處理呢? –給立即執行函數來處理
方案:我們可以寫一個立即執行函數,因爲我們儘量的避免污染全局變量,所以把模塊modules給立即執行函數,來處理裏面的模塊

var modules = {
	"./src/a.js ": function(){ //...}
}
(function(modules){})(modules)   //把modules 給立即執行函數,來執行來處理裏面的所有模塊

問題4:var modules = {} 然後傳參給立即執行函數,會有全局變量污染
方案:爲了不污染全局變量,直接把modules對應的內容,作爲字面量傳給立即執行函數

(function(modules){ })({
	"./src/a.js ": function(){ //...}
})   //把modules 給立即執行函數,來執行來處理裏面的所有模塊

這樣就不會污染全局變量,同時又把模塊構建好了,所有的模塊對保存在對象裏{模塊的路徑:模塊的函數},然後把這個對象給立即執行函數
---------------step3-----------------------------------------

3、手寫執行函數,執行入口模塊

立即執行函數裏面要執行入口模塊 require('./src/index.js) --require(’./src/a.js)就是找到./src/a.js對應的函數去執行
require函數相當於運行一個模塊,並得到模塊的導出結果,這個require函數要自己寫

(function(modules){
	    function webpack_require(moduleId){   //moduleId就是模塊的路徑,爲了避免require  和node環境的require重名,改用webpack_require
        var func = modules[moduleId];   //得到模塊對應的函數
        var module = {
            exports:{

            }
        }
        var exports = module.exports;
        func(module,exports,webpack_require); //執行模塊對應的函數,傳入參數
        var result = modules.exports; //得到模塊導出的結果
        return result;
    }
    //執行入口模塊
    return __webpack_require("./src/index.js"); //require函數相當於是運行一個模塊,得到模塊導出結果
})({
	"./src/a.js ": function(){ //...}
})   //把modules 給立即執行函數,來執行來處理裏面的所有模塊

---------------step4-----------------------------------------

4、加載模塊緩存問題

問題:加載模塊緩存問題?就是一個模塊加載了多次怎麼辦?

(function(modules){
    var moduleExports = {
        // "./src/a.js":'a'   //這種方式緩存結果
    };//1-----------------用於緩存模塊的導出結果
    
    function webpack_require(moduleId){   //moduleId就是模塊的路徑,爲了避免require  和node環境的require重名,改用webpack_require
        if(moduleExports[moduleId]){
            //2-------------- 檢查是否有緩存
            return moduleExports[moduleId];
        }
        //3-----------如果沒有
        var func = modules[modules];   //得到模塊對應的函數
        var module = {
            exports:{

            }
        }
        var exports = module.exports;
        func(module,exports,webpack_require); //執行模塊對應的函數,傳入參數
        var result = modules.exports; //得到模塊導出的結果
        moduleExports[moduleId] = result; //4-----------緩存結果
        return result;
    }
	//執行入口模塊
    return __webpack_require("./src/index.js"); //require函數相當於是運行一個模塊,得到模塊導出結果
})({   
    "./src/a.js ": function(module,exports,require){
        console.log('a 模塊');
        module.exports = 'a';
    },     
    "./src/index.js ": function(module,exports,require){
        var a = require('./src/a.js');  
        console.log(a);
        
        console.log('index模塊');
    },   
}) 

---------------step5-----------------------------------------

5、模塊裏的代碼放到eval()裏,方便調試

問題:爲什麼模塊合併後,把模塊裏的代碼放到對應函數裏的eval()裏面?
這和瀏覽器有關,最終運行的是合併後的代碼,如果報錯了只能指示到打包文件裏,爲了更好的調試,查找報錯位置,放在eval()裏。
eval()裏的代碼是放到另一個環境執行的,如果報錯,就看不到其他代碼的干擾了

"./src/a.js": function (module, exports) {
     eval("console.log(\"module a\")\nmodule.exports = \"a\";\n //# sourceURL=webpack:///./src/a.js")
 },

//# sourceURL= webpack:///./src/a.js 告訴瀏覽器觸發調試的時候,顯示的錯誤位置路徑是./src/a.js

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章