webpack4 es module打包后代码分析

1.单一模块

index.js

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

打包后输出的文件(eval sourcemap部分删除)

(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);");
    })
});

打包后的文件为一个自执行的函数匿名,参数为入口文件及其依赖的模块(此处没有依赖模块)

(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:判断对象上是否有某一属性

__webpack_require__函数

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包含如下内容
图片描述

参数

{
    "./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.js

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...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值