以下内容所使用的webpack版本是4.43。
先看下同步加载代码。
入口文件index.js:
1 2
| import text from './sync.js' console.log(text)
|
被导入文件sync.js:
1 2
| const text = 'sync'; export default text;
|
打包后获得如下的文件:index.bundle.js(先忽略细节,看下整体结构)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| (function (modules) { var installedModules = {}; function __webpack_require__(moduleId) { return module.exports; } return __webpack_require__(__webpack_require__.s = "./src/index.js"); }) ({ "./src/index.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _syncc_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "./src/syncc.js"); console.log(_syncc_js__WEBPACK_IMPORTED_MODULE_0__["default"]); }), "./src/sync.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var text = 'sync'; __webpack_exports__["default"] = (text);
}) });
|
可以看出,打包出的源码就是一个立即执行函数,前半部分是方法主体,后半部分是方法入参。入参是一个对象,对象的key是被打包文件的路径,而value值为一个方法,方法内运行的是模块内容。姑且把整个对象叫做模块对象。
在前半部分的函数体结尾,执行了__webpack_require__(__webpack_require__.s = "./src/index.js");
方法,整个程序其实也是从这里开始。
__webpack__require(moduleId)
:根据传入的moduleId加载对应的模块并缓存,最后返回模块导出的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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__);
module.l = true;
return module.exports; }
|
上面的方法中比较重要的是下面这一句:
1
| modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
读取moduleId
对应的模块加载方法并执行。
modules
就是传入进来的模块对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "./src/index.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _syncc_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "./src/syncc.js"); console.log(_syncc_js__WEBPACK_IMPORTED_MODULE_0__["default"]); }), "./src/sync.js": (function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var text = 'sync'; __webpack_exports__["default"] = (text); }) }
|
所以,modules['./src/index.js'].call(...)
其实就是执行的下面这个函数:
1 2 3 4 5 6 7
| function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _syncc_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "./src/sync.js"); console.log(_syncc_js__WEBPACK_IMPORTED_MODULE_0__["default"]); }
|
上面方法中先是执行了__webpack_require__.r
:
1 2 3 4 5 6 7 8 9
|
__webpack_require__.r = function (exports) { if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); } Object.defineProperty(exports, '__esModule', { value: true }); };
|
接下来就是index.js源代码文件中的内容了:
1 2 3
| var _syncc_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( "./src/sync.js"); console.log(_syncc_js__WEBPACK_IMPORTED_MODULE_0__["default"]);
|
先是导入sync.js
中的内容,然后打印输出所导入内容,和index.js
源码内容一致:
1 2
| import text from './syncc.js' console.log(text)
|
在导入sync.js
时,依然是使用__webpack_require__()
方法,入参是./src/sync.js
:
1 2
| __webpack_require__("./src/sync.js");
|
同样的,加载sync.js
时也是先去缓存中看一下之前有没有加载过这个模块,没有的话再去加载:
1
| modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
执行对应的模块加载方法:
1 2 3 4 5 6
| function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var text = 'sync'; __webpack_exports__["default"] = (text); }
|
可以看到模块导出的内容挂在__webpack_exports__
对象下面:
1
| __webpack__exports__["default"] = (text);
|
对于每个没有加载过的模块,在加载之前都会创建一个module
对象:
1 2 3 4 5 6
| var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} };
|
而模块加载方法内的__webpack__exports__
就是传入进来的module.exports
对象,在__webapck__require__()
方法最后返回module.exports
,所以要导出的内容就被返回了。
总结一下,每个模块在编译之后都是一个函数,这些函数挂在模块对象下,以自己的文件路径为属性key,类似下面的结构:
1 2 3 4 5 6 7 8 9 10
| { './src/index.js': (function(){ }), .. [moduleId]: (function(){ }) }
|
模块的加载过程主要用到的就是__webpack_require__(moduleId)
这个方法。加载流程就是先加载入口模块(index.js),如果当前模块内部又引用了其他模块(sync.js),则继续使用__webpack_require__(moduleId)
去加载,这样一直递归加载下去,直到所有模块都加载完毕。对于每个加载过的模块都会被缓存起来(存储在installedModules[moduleId]
下),后续再次用到时会直接去缓存中获取而不用再去执行一次模块。