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对象中