详细解析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);
};
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/1508.html