webpack如何管理所有所需模块之间的交互?
- 首先webpack添加了一些用于管理模块的代码(称为runtime)
__webpack_require__
是一个模块加载函数,模拟 require 和 import, 该函数接收一个moduleId参数,用于执行js模块。__webpack_require__.m
或__webpack_modules__
对象,该对象存放一个chunk下的module集合
- 除了上面列举的变量和函数外,还有其他更多.
- 当浏览器加载一个业务代码相关的js文件,webpack会向一个全局二维数组中添加一个一维数组,该一维数组的第一项是chunkIds,第二项是一个modules对象,第三项是一个函数。
- modules对象,该对象的key是moduleId, value是一个函数,该函数的参数是
module, __webpack_exports__, __webpack_require__
,函数内的代码是一个js文件或者多个js文件内的代码。
- 第二项的modules对象会复制到
__webpack_modules__
对象上。 - 执行第三项函数,该函数会通过
__webpack_require__[moduleId]
执行入口模块代码。
__webpack_require__
函数会先通过moduleId
在__webpack_modules__
对象上找到入口模块代码,然后执行它。
- 入口模块代码中也是通过
__webpack_require__[moduleId]
加载其他模块
源码详细分析
webpack打包后业务代码结构如下
js
// push的一个数组
// 第一项是chunkIds
// 第二项是modules对象,key是moduleId, value是一个函数,函数中就是module的代码
// 第三项是一个函数,会执行入口模块代码
// 代码中用到的__webpack_require__是在runtime中定义的
"use strict";
(self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || []).push([[201],{
/***/ 152:
/***/ (() => {
// 一个module不一定是一个js文件,可能是多个js文件合并的
;// CONCATENATED MODULE: ./src/src/test.js
function test() {
console.log(111)
}
;// CONCATENATED MODULE: ./src/entry.js
test()
/***/ })
},
__webpack_require__ => { // webpackRuntimeModules
var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
var __webpack_exports__ = (__webpack_exec__(152)); // 执行入口模块
}
]);
window.webpackChunkwebpack_demo全局二维数组
数组的每一项是一个chunk
如下图:
- window上挂载一个变量
webpackChunkwebpack_demo
;该变量是一个数组
webpack的runtime代码的基本结构
代码执行顺序
- 首先加载runtime的js文件,执行该文件
- 给window.webpackChunkwebpack_demo的push赋值
js
var chunkLoadingGlobal = self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || [];
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
javascript
在此复习下`Function.prototype.bind()`的用法
Function的实例bind()方法创建一个新函数,
当调用新函数时,它会调用原始函数并将其this关键字设置为给定的值,
同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
-
加载业务代码的js文件
-
执行入口函数,通过
__webpack_require__(moduleId)
加载依赖文件
- 执行window.webpackChunkwebpack_demo.push方法,即执行webpackJsonpCallback方法
js
(self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || []).push([[792],{},)
- 执行webpackJsonpCallback
- 即执行webpackJsonpCallback方法接收的第一个参数是chunkIds,第二个参数是modules对象
- 将moreModules加入到
__webpack_require__.m
模块对象中 - moreModules对象是调用
window.webpackChunkwebpack_demo.push()
传入的第二个参数,是一个对象,该对象的key是moduleId(一个js文件对应的id),对象的value是该js文件内的代码,被一个函数包裹。webpack将所有用到的文件到存在该对象中。最后放到__webpack_require__.m
对象中。 - 执行
window.webpackChunkwebpack_demo.push()
传入的第三个参数,第三个参数是一个函数,在该函数中会执行入口文件。
js
// install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
....
/******/ }
window.webpackChunkwebpack_demo.push()
传入的第三个参数如下:
- 定义了__webpack_exec__,它是一个函数, 参数是moduleId,内部执行__webpack_require__方法,参数是moduleId
- 执行__webpack_exec__(960),即执行入口js文件。
- 执行结果赋值给__webpack_exports__
js
/******/ __webpack_require__ => { // webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
/******/ var __webpack_exports__ = (__webpack_exec__(960));
/******/ }
以下是__webpack_require__函数的定义:
__webpack_module_cache__
是一个对象,key是moduleId,value是一个对象,该对象上有id和exports两个key,id
是moduleId
,exports
一个引用,指向模块的导出对象。如果一个js模块已经加载,就直接从缓存中取出使用。__webpack_modules__
和__webpack_require__.m
是同一个对象- 执行
__webpack_modules__[moduleId]()
就是执行对应的js文件代码
js
// The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ id: moduleId,
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
//__webpack_modules__ 就是当前chunkIds下的所有模块集合,用对象表示,key是moduleId,value是一个函数,函数内是js模块代码,执行该函数就是记载一个模块。
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
以下是将一个js文件中的代码封装到一个方法中的实现
js
const fs = require('fs');
const path = require('path');
let file = path.resolve(__dirname, 'bind.js')
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
console.error('Error reading the file:', err);
return;
}
let obj = {}
obj[1] = new Function("" + data)
obj[1]()
});