详细解析Webpack是怎么运行的(5)
可以看到 webpackBootstrap 的函数体部分增加了一些内容,参数部分移除了"./src/utils/math.js"模块。跟着包裹函数的执行顺序,我们先聚焦到「JSONP 初始化」部分:
// 存储 jsonp 的数组,首次运行为 [] var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; // 保存 jsonpArray 的 push 函数,首次运行为 Array.prototype.push var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); // 将 jsonpArray 的 push 重写为 webpackJsonpCallback (加载其他 chunk 后的回调函数) jsonpArray.push = webpackJsonpCallback; // 将 jsonpArray 重置为正常数组,push 重置为 Array.prototype.push jsonpArray = jsonpArray.slice(); // 由于 jsonpArray 为 [],不做任何事 for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); // Array.prototype.push var parentJsonpFunction = oldJsonpFunction;
初始化结束后,变化就是 window 上挂载了一个 webpackJsonp 数组,它的值为[];此外,这个数组的 push 被改写为 webpackJsonpCallback 函数,我们在后面会提到这些准备工作的作用。
接着是 __webpack_require__入口模块,由于 __webpack_require__ 函数没有改变,我们继续观察入口模块执行函数有了什么变化。
显然,import('../utils/math.js') 被转化为__webpack_require__.e(0).then(__webpack_require__.bind(null, "./src/utils/math.js"))。0 是 ./src/utils/math.js 所在 chunk 的id,「同步加载模块」的逻辑拆分成了「先加载 chunk,完成后再加载模块」。
我们翻到 __webpack_require__.e的定义位置:
__webpack_require__.e = function requireEnsure(chunkId) { var promises = []; // installedChunks 是在 webpackBootstrap 中维护的 chunk 缓存 var installedChunkData = installedChunks[chunkId]; // chunk 未加载 if(installedChunkData !== 0) { // installedChunkData 为 promise 表示 chunk 加载中 if(installedChunkData) { promises.push(installedChunkData[2]); } else { /*** 首次加载 chunk: ***/ // 初始化 promise 对象 var promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); promises.push(installedChunkData[2] = promise); // 创建 script 标签加载 chunk var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); var onScriptComplete; // ... 省略一些 script 属性设置 // src 根据 publicPath 和 chunkId 拼接 script.src = jsonpScriptSrc(chunkId); // 加载结束回调函数,处理 script 加载完成、加载超时、加载失败的情况 onScriptComplete = function (event) { script.onerror = script.onload = null; // 避免 IE 内存泄漏问题 clearTimeout(timeout); var chunk = installedChunks[chunkId]; // 处理 script 加载完成,但 chunk 没有加载完成的情况 if(chunk !== 0) { // chunk 加载中 if(chunk) { var errorType = event && (event.type === 'load' ? 'missing' : event.type); var realSrc = event && event.target && event.target.src; var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'); error.type = errorType; error.request = realSrc; // reject(error) chunk[1](error); } // 统一将没有加载的 chunk 标记为未加载 installedChunks[chunkId] = undefined; } }; // 设置 12 秒超时时间 var timeout = setTimeout(function(){ onScriptComplete({ type: 'timeout', target: script }); }, 120000); script.onerror = script.onload = onScriptComplete; head.appendChild(script); /*** 首次加载 chunk ***/ } } return Promise.all(promises); };
内容版权声明:除非注明,否则皆为本站原创文章。