详细解析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);
};
内容版权声明:除非注明,否则皆为本站原创文章。
