// 异步加载函数挂载在 __webpack_require__.e 上 __webpack_require__.e = function requireEnsure(chunkId, callback) { if(installedChunks[chunkId] === 0) return callback.call(null, __webpack_require__); if(installedChunks[chunkId] !== undefined) { installedChunks[chunkId].push(callback); } else { installedChunks[chunkId] = [callback]; var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.type = 'text/javascript'; script.charset = 'utf-8'; script.async = true; script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js"; head.appendChild(script); } };
大致分为三种情况,(已经加载过,正在加载中以及从未加载过)
- 已经加载过该 chunk 文件,那就不用再重新加载该 chunk 了,直接执行回调函数即可,可以理解为假如页面有两种操作需要加载加载异步脚本,但是两个脚本都依赖于公共模块,那么第二次加载的时候发现之前第一次操作已经加载过了该 chunk,则不用再去获取异步脚本了,因为该公共模块已经被执行过了。
- 从未加载过,则动态地去插入 script 脚本去请求 js 文件,这也就为什么取名 webpackJsonpCallback ,因为跟 jsonp 的思想很类似,所以这种异步加载脚本在做脚本错误监控时经常出现 Script error,具体原因可以查看我之前写的文章:前端代码异常监控实战
- 正在加载中代表该 chunk 文件已经在加载中了,比如说点击按钮触发异步脚本,用户点太快了,连点两次就可能出现这种情况,此时将回调函数放入 installedChunks。
我们通过 utilC 生成的 chunk 来进行讲解:
webpackJsonp([2,4],{ 4: (function(module, exports) { module.exports = "util C"; }) });
pageB 需要异步加载这个 chunk:
webpackJsonp([1,4],[ /* 0 */ (function(module, exports, __webpack_require__) { const utilB = __webpack_require__(3); console.log(utilB); const utilC = () => __webpack_require__.e/* nsure */(2, function(require) { console.log(__webpack_require__(4)) }); utilC(); }) ]);
当 pageB 进行某种操作需要加载 utilC 时就会执行 __webpack_require__.e(2, callback) 2,代表需要加载的模块 chunkId(utilC),异步加载 utilC 并将 callback 添加到 installedChunks[2] 中,然后当 utilC 的 chunk 文件加载完毕后,chunkIds 包含 2,发现 installedChunks[2] 是个数组,里面还有之前还未执行的 callback 函数。
既然这样,那我就将我自己带来的模块先放到 modules 中,然后再统一执行之前未执行完的 callbacks 函数,这里指的是存放于 installedChunks[2] 中的回调函数 (可能存在多个),这也就是说明这里的先后顺序: