webpack4 es module打包後代碼分析

1.單一模塊

index.jswebpack

export let num = 9;
let def = 44;
export default def;

打包後輸出的文件(eval sourcemap部分刪除)es6

(function(modules) { // webpackBootstrap

    var installedModules = {};

    function __webpack_require__(moduleId) {

        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }

        var module = installedModules[moduleId] = {
            i: moduleId,  
            l: false,     
            exports: {}
        };

        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);console.log(module)

        module.l = true;

        return module.exports;
    }

    __webpack_require__.m = modules;

    __webpack_require__.c = installedModules;

    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };

    __webpack_require__.r = function(exports) {
        Object.defineProperty(exports, '__esModule', { value: true });
    };

    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    __webpack_require__.o = function(object, property) {
        return Object.prototype.hasOwnProperty.call(object, property);
    };

    __webpack_require__.p = "";

    return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {

        "use strict";

        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"num\", function() { return num; });\n let num = 9;\r\n let def = 44;\r\n __webpack_exports__[\"default\"] = (def);");
    })
});

打包後的文件爲一個自執行的函數匿名,參數爲入口文件及其依賴的模塊(此處沒有依賴模塊)web

(function(modules) {
    var installedModules = {};
    
    function __webpack_require__(moduleId) {
        ...
    }
    
    __webpack_require__.m = modules;
    
    __webpack_require__.c = installedModules;
    
    __webpack_require__.d = function(){
        ...
    }
    
    __webpack_require__.r = function(){
        ...
    }
    
    __webpack_require__.n = function(){
        ...
    }
    
    __webpack_require__.o = function(){
        ...
    }
    
    __webpack_require__.p = '';
    
    return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("...");

})

匿名函數聲明瞭一個installedModules對象,用於緩存加載進來的模塊(加載進來的模塊不必定加載完成)
聲明瞭__webpack_require__,併爲其添加了一些方法和屬性:m、c、d、r、n、o
m:保存傳入的模塊對象
c:保存緩存的模塊
d:在exports對象上添加屬性
r:在exports對象上添加__esModule,用於標識es6模塊
n:getDefaultExport
o:判斷對象上是否有某一屬性json

__webpack_require__函數segmentfault

function __webpack_require__(moduleId) {

        //是否有緩存
        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }

        var module = installedModules[moduleId] = {
            i: moduleId,   //模塊id
            l: false,     //模塊是否已加載
            exports: {}
        };

        //加載模塊
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        module.l = true;

        return module.exports;
    }

index.js模塊裏的東西經過modules[moduleId].call()中加載進來,最終的module包含以下內容
圖片描述數組

參數promise

{
    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {

        //es6模塊爲嚴格模式
        "use strict";

        //每一個模塊都被eval執行
        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"num\", function() { return num; });\n let num = 9;\r\n let def = 44;\r\n __webpack_exports__[\"default\"] = (def);");
        
        /*eval中執行了如下內容
        * __webpack_require__.r(__webpack_exports__);
        * __webpack_require__.d(__webpack_exports__, "num", function() { return num; });
        * let num = 9;
        * let def = 44;
        * __webpack_exports__["default"] = def;
        * */
    }

"./src/index.js"爲文件路徑也是該模塊的id
eval中爲經過__webpack_require__.r給export添加__esModule屬性,並定義num、default屬性緩存

2.依賴模塊

chunk.jsapp

let def = 44;
export default def;

index.js函數

import def from './chunk1.js';
export let num = 9;

打包後的文件(自執行函數體部分不變,僅展現參數部分)

{
    "./src/chunk1.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n let def = 44;\r\n __webpack_exports__[\"default\"] = (def);");
    }),

    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"num\", function() { return num; });\n var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/chunk.js\");\n\r\n let num = 9;\r\n console.log(_chunk1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])");
    })
}

index.js模塊中調用__webpack_require__("./src/chunk1.js")加載chunk.js,最終installedModules以下
clipboard.png

3.依賴重複模塊

index.js

import chunk1 from './chunk1.js';
import chunk2 from './chunk2.js';
export let index1 = 9;
console.log(chunk1);
console.log(chunk2);

chunk1.js

import chunk2 from './chunk2.js';
let chunk1 = 1;
export default chunk1;

chunk2.js

let chunk2 = 2;
export default chunk2;

打包後的參數部分

({
    "./src/chunk1.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n var _chunk2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/chunk2.js\");\n\r\n let chunk1 = 1;\r\n __webpack_exports__[\"default\"] = (chunk1);");
    }),

    "./src/chunk2.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n let chunk2 = 2;\r\n __webpack_exports__[\"default\"] = (chunk2);");

    }),

    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"index1\", function() { return index1; });\n var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/chunk1.js\");\n var _chunk2_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(\"./src/chunk2.js\");\n\r let index1 = 9;\r\n console.log(_chunk1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\r\n console.log(_chunk2_js__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);");

    })
});

模塊不會重複加載,chunk1中import chunk2.js __webpack_require__直接返回緩存中的數據

4.入口爲數組

(function(modules) {
    ...
    return __webpack_require__(__webpack_require__.s = 0);
})({
    "./src/chunk1.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n let chunk1 = 1;\r\n __webpack_exports__[\"default\"] = (chunk1);");
    }),

    "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"index1\", function() { return index1; });\n var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/chunk1.js\");\n\r\nlet index1 = 9;\r\n console.log(_chunk1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);");
    }),

    "./src/index2.js": (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n __webpack_require__.d(__webpack_exports__, \"index2\", function() { return index2; });\n var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./src/chunk1.js\");\n\r\nlet index2 = 66;");
    }),

    0: (function(module, exports, __webpack_require__) {
        eval("__webpack_require__(\"./src/index.js\");\n module.exports = __webpack_require__(\"./src/index2.js\");");
    })
});

相比字符串入口return的再也不是index.js,而是moduleId爲0的模塊,在0模塊中觸發index、index2模塊的加載並返回index2模塊(數組中的最後一項)

5.使用splitChunks

runtimeChunk爲true

index.bundle.js

(function(modules) {
    // install a JSONP callback for chunk loading
    function webpackJsonpCallback(data) {
        var chunkIds = data[0];
        var moreModules = data[1];
        var executeModules = data[2];
        // add "moreModules" to the modules object,
        // then flag all "chunkIds" as loaded and fire callback
        var moduleId, chunkId, i = 0, resolves = [];
        for(;i < chunkIds.length; i++) {
            chunkId = chunkIds[i];
            if(installedChunks[chunkId]) {
                resolves.push(installedChunks[chunkId][0]);
            }
            installedChunks[chunkId] = 0;
        }
        for(moduleId in moreModules) {
            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                modules[moduleId] = moreModules[moduleId];
            }
        }
        if(parentJsonpFunction) parentJsonpFunction(data);
        while(resolves.length) {
            resolves.shift()();
        }

        // add entry modules from loaded chunk to deferred list
        deferredModules.push.apply(deferredModules, executeModules || []);

        // run deferred modules when all chunks ready
        return checkDeferredModules();
    };

    function checkDeferredModules() {
        var result;
        for(var i = 0; i < deferredModules.length; i++) {
            var deferredModule = deferredModules[i];
            var fulfilled = true;
            for(var j = 1; j < deferredModule.length; j++) {
                var depId = deferredModule[j];
                if(installedChunks[depId] !== 0) fulfilled = false;
            }
            if(fulfilled) {
                deferredModules.splice(i--, 1);
                result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
            }
        }

        return result;
    }

    var installedModules = {};

    // object to store loaded and loading chunks
    // undefined = chunk not loaded, null = chunk preloaded/prefetched
    // Promise = chunk loading, 0 = chunk loaded
    var installedChunks = {
        "runtime~main": 0
    };

    // script path function
    function jsonpScriptSrc(chunkId) {
        return __webpack_require__.p + "" + chunkId + ".index.bundle.js"
    }

    var deferredModules = [];

    // The require function
    function __webpack_require__(moduleId) {

        // Check if module is in cache
        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };

        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        // Flag the module as loaded
        module.l = true;

        // Return the exports of the module
        return module.exports;
    }

    __webpack_require__.m = modules;

    __webpack_require__.c = installedModules;

    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };

    __webpack_require__.r = function(exports) {
        Object.defineProperty(exports, '__esModule', { value: true });
    };

    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    __webpack_require__.p = "";

    var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];

    var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);

    //改寫push方法,main中調用該方法
    jsonpArray.push = webpackJsonpCallback;
    jsonpArray = jsonpArray.slice();

    //main.index.bundle.js push以前jsonpArray爲空
    for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);

    //保留原始的push方法,webpackJsonpCallback中經過該方法將main.inde.bundle.js中的數組添加進window["webpackJsonp"]
    var parentJsonpFunction = oldJsonpFunction;

    // run deferred modules from other chunks
    checkDeferredModules();
})([]);

總體爲一個自執行函數,所以時參數爲空,index.bundle.js中只是綁定了一些屬性、方法,並未加載具體的模塊。
installedModules緩存已加載的模塊
installedChunks緩存chunnk,值爲undefined表示模塊還沒有加載,null表示preloaded/prefetched,promise表示加載中,0表示已加載
webpackJsonpCallback中處理數據綁定及回調
checkDeferredModules中檢測chunks的加載狀況,所有loaded後調用__webpack_require__('./src/index.js')處理具體模塊的加載(./src/index.js爲入口模塊的id)

main.index.bundle.js

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
    ["main"],
    {
        "./src/chunk1.js": (function(module, __webpack_exports__, __webpack_require__) {
            "use strict";
            eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _chunk2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chunk2.js */ \"./src/chunk2.js\");\n\r\nlet chunk1 = 1;\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (chunk1);\n\n//# sourceURL=webpack:///./src/chunk1.js?");
        }),

        "./src/chunk2.js": (function(module, __webpack_exports__, __webpack_require__) {
            "use strict";
            eval("__webpack_require__.r(__webpack_exports__);\nlet chunk2 = 2;\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (chunk2);\n\n//# sourceURL=webpack:///./src/chunk2.js?");
        }),

        "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) {
            "use strict";
            eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"index1\", function() { return index1; });\n/* harmony import */ var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chunk1.js */ \"./src/chunk1.js\");\n/* harmony import */ var _chunk2_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./chunk2.js */ \"./src/chunk2.js\");\n\r\n\r\nlet index1 = 9;\r\nconsole.log(_chunk1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n//# sourceURL=webpack:///./src/index.js?");
        })
    },
    [["./src/index.js","runtime~main"]]
]);

window["webpackJsonp"].push會調用webpackJsonpCallback方法

6.動態加載

index.js

import chunk1 from './chunk1.js';
export let index1 = 9;
import('./chunk2.js').then(res=>{
    console.log(res);
}).catch(e=>{
    console.log(e)
})

打包後的代碼僅main.index.bundle.js的"./src/index.js"eval部分不一樣

__webpack_require__.r(__webpack_exports__);
        __webpack_require__.d(__webpack_exports__, "index1", function() { return index1; });
        var _chunk1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/chunk1.js");
        let index1 = 9;
        Promise.resolve()
            .then(__webpack_require__.bind(null, "./src/chunk2.js"))
            .then(res=>{
                console.log(res);
            })
            .catch(e=>{
                console.log(e)
            });

import()的promise直接resolve,回調在then(__webpack_require__.bind(null, "./src/chunk2.js"))後執行

參考文章:https://segmentfault.com/a/11...

相關文章
相關標籤/搜索