详细解析Webpack是怎么运行的(3)

首先,加载函数使用了闭包变量 installedModules,用来将已加载过的模块保存在内存中。 接着是初始化模块对象,并把它挂载到缓存里。然后是模块的执行过程,加载入口文件时 modules[moduleId] 其实就是./src/index.js对应的模块函数。执行模块函数前传入了跟模块相关的几个实参,让模块可以导出内容,以及加载其他模块的导出。最后标识该模块加载完成,返回模块的导出内容。

根据 __webpack_require__ 的缓存和导出逻辑,我们得知在整个 IIFE 运行过程中,加载已缓存的模块时,都会直接返回installedModules[moduleId].exports,换句话说,相同的模块只有在第一次引用的时候才会执行模块本身。

模块执行函数

__webpack_require__中通过 modules[moduleId].call() 运行了模块执行函数,下面我们就进入到webpackBootstrap 的参数部分,看看模块的执行函数。

/*** 入口模块 ./src/index.js ***/
"./src/index.js": (function (module, __webpack_exports__, __webpack_require__) {
 "use strict";
// 用于区分 ES 模块和其他模块规范,不影响理解 demo,战略跳过。
 __webpack_require__.r(__webpack_exports__);
 /* harmony import */
 // 源模块代码中,`import {plus} from './utils/math.js';` 语句被 loader 解析转化。
 // 加载 "./src/utils/math.js" 模块,
 var _utils_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils/math.js");
 console.log('Hello webpack!');
 console.log('1 + 2: ', Object(_utils_math_js__WEBPACK_IMPORTED_MODULE_0__["plus"])(1, 2));
}),
"./src/utils/math.js": (function (module, __webpack_exports__, __webpack_require__) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */
// 源模块代码中,`export` 语句被 loader 解析转化。
 __webpack_require__.d(__webpack_exports__, "plus", function () {
 return plus;
 });
 const plus = (a, b) => {
 return a + b;
 };
})

执行顺序是:入口模块 -> 工具模块 -> 入口模块。入口模块中首先就通过 __webpack_require__("./src/utils/math.js") 拿到了工具模块的 exports 对象。再看工具模块,ES 导出语法转化成了__webpack_require__.d(__webpack_exports__, [key], [getter]),而 __webpack_require__.d 函数的定义在 webpackBootstrap 内:

// 定义 exports 对象导出的属性。
 __webpack_require__.d = function (exports, name, getter) {
 // 如果 exports (不含原型链上)没有 [name] 属性,定义该属性的 getter。
 if (!__webpack_require__.o(exports, name)) {
  Object.defineProperty(exports, name, {
  enumerable: true,
  get: getter
  });
 }
 };
 // 包装 Object.prototype.hasOwnProperty 函数。
 __webpack_require__.o = function (object, property) {
 return Object.prototype.hasOwnProperty.call(object, property);
 };

可见 __webpack_require__.d 其实就是 Object.defineProperty 的简单包装.