webpack是当下最流行的模块打包工具,webpack处理应用程序时候,会递归构建一个依赖关系图,其中包含应用程序的每个模块,然后将这些模块打包成一个或者多个bundle

本文使用的webpack版本是4.8.3

// src/a.js
const a = 'a module';
export default a;
// src/index.js
import a from './a'
console.log(a);
console.log('index');

执行webpack --mode development,在dist文件夹会打包成一个打包后的main.js文件,以下是打包后的文件

/**
 *  传入一个modules对象
 **/
(function (modules) {
    // 定义一个变量缓存模块
    var installedModules = {};

    // require函数
    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__);
        // 标记已经加载完毕
        module.l = true;
        // 返回模块
        return module.exports;
    }

    //  modules对象
    __webpack_require__.m = modules;
    // 模块缓存对象
    __webpack_require__.c = installedModules;
    // define getter function for harmony exports
    __webpack_require__.d = function (exports, name, getter) {
        if (!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };

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

    // getDefaultExport function for compatibility with non-harmony modules
    __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_public_path__
    __webpack_require__.p = "";

    // 加载入口模块,并且返回该模块
    return __webpack_require__(__webpack_require__.s = "./webpack/index.js");
})({
    "./webpack/a.js": (function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\nconst a = 'a module';\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (a);\n\n//# sourceURL=webpack:///./webpack/a.js?");
    }),
    "./webpack/index.js": (function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ \"./webpack/a.js\");\n\r\nconsole.log(_a__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\r\nconsole.log('index');\n\n//# sourceURL=webpack:///./webpack/index.js?");
    })
});

从去除注释后的代码看出,模块打包后是一个立即执行函数,传入的参数为一个对象

{
    "./webpack/a.js": function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        __webpack_require__.r(__webpack_exports__);
        const a = 'a module';
        __webpack_exports__["default"] = (a);
    },
    "./webpack/index.js": function (module, __webpack_exports__, __webpack_require__) {
        "use strict";
        __webpack_require__.r(__webpack_exports__);

        // 这里找到了依赖模块./webpack/a.js,执行模块./webpack/a.js的代码
        var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./webpack/a.js");
        console.log(_a__WEBPACK_IMPORTED_MODULE_0__["default"]);
        console.log('index');
    }
}

在IIFE函数最后return的时候会去调用__webpack_require__函数,传入的是入口文件./webpack/index.js这个就是作为模块的id,然后__webpack_require__就会去执行下面入口模块的代码

/**
 *  @module 模块对象 { i: moduleId, l: false, exports: {} }
 *  @__webpack__exports__  相当于 module.exports 一个空的对象
 *  @__webpack__require__  require函数
 */
(function (module, __webpack_exports__, __webpack_require__) {
    "use strict";
    __webpack_require__.r(__webpack_exports__);

    // 这里找到了依赖模块./webpack/a.js,执行模块./webpack/a.js的代码
    var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./webpack/a.js");
    console.log(_a__WEBPACK_IMPORTED_MODULE_0__["default"]);
    console.log('index');
})

接下来index.js依赖于模块a,继续执行./webpack/a.js的代码,返回以下新的对象,用变量_a__WEBPACK_IMPORTED_MODULE_0__保存起来,a.js执行后返回的对象的结构为

{
    i: "",
    l: true,
    exports: {
        default: 'a module',    // 这个例子返回的就是一个常量
        __esModule: true
    }
}

总结

  1. webpack会把所有的模块用一个对象来表示,类似这种格式 { moduleId: function(module, module.exports, require) { // 模块对应的代码 }; },传入到立即执行函数中
  2. 从入口文件开始执行代码,遇到有模块依赖的时候,会去缓存对象installedModules里面查找依赖的模块对象来执行,如果没有找到就执行模块的代码并把依赖的模块缓存在installedModules对象中