可以看到 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.
- ( + 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);
- };
看起来有点长,我们一步步剖析,先从第一行和最后一行来看,整个函数将异步加载的过程封装到了 promise 中,最终导出。
(编辑:ASP站长网)
|