webpack 是当下最流行的模块打包工具,webpack 处理应用程序时候,会递归构建一个依赖关系图,其中包含应用程序的每个模块,然后将这些模块打包成一个或者多个 bundle
本文使用的 webpack 版本是 4.8.3
// src/a.jsconst a = 'a module';export default a;
// src/index.jsimport 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 }}
总结
- webpack 会把所有的模块用一个对象来表示,类似这种格式
{ moduleId: function(module, module.exports, require) { // 模块对应的代码 }; }
,传入到立即执行函数中 - 从入口文件开始执行代码,遇到有模块依赖的时候,会去缓存对象
installedModules
里面查找依赖的模块对象来执行,如果没有找到就执行模块的代码并把依赖的模块缓存在installedModules
对象中