假如通过splitChunks分出了多个initialChunk,Webpack如何保证这些产物的加载顺序?下文参考这个仓库,构建产物包含app.js和vendors.js。webpack如何保证在下载了vendors.js之后才执行app.js相关的模块代码?
app.js中有如下代码
1 2 3 4
| var __webpack_exports__ = __webpack_require__.O(undefined, ["vendors"], function() { return __webpack_require__("./client/src/entry.tsx"); })
|
webpack通过JSONP的方式加载chunk,__webpack_require__.O 是webpack管理runtime chunk加载的函数,会将entry所依赖的chunk以及dependency chunk loaded callback记录到deferred数组中,dependency chunk加载完成后便执行callback。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| !function() { var deferred = []; __webpack_require__.O = function(result, chunkIds, fn, priority) { if (chunkIds) { priority = priority || 0; for (var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; deferred[i] = [chunkIds, fn, priority]; return; } var notFulfilled = Infinity; for (var i = 0; i < deferred.length; i++) { var chunkIds = deferred[i][0]; var fn = deferred[i][1]; var priority = deferred[i][2]; var fulfilled = true; for (var j = 0; j < chunkIds.length; j++) { if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every(function(key) { return __webpack_require__.O[key](chunkIds[j]); })) { chunkIds.splice(j--, 1); } else { fulfilled = false; if (priority < notFulfilled) notFulfilled = priority; } } if (fulfilled) { deferred.splice(i--, 1) var r = fn(); if (r !== undefined) result = r; } } return result; }; }();
|
chunk加载
vendors.js下载完执行时会调用window[chunkLoadingGlobal].push相关模块。
chunkLoadingGlobal这个键名可以通过output.chunkLoadingGlobal更改。window[chunkLoadingGlobal]是在app.js中实现的(假如webpack配置开启了optimization.runtimeChunk,则在单独的runtimeChunk.js文件中),将chunk相关的modules添加到全局模块缓存后,会check一遍deferred数组中是否有需要执行的callback,我们这个例子中就是执行entry.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
var webpackJsonpCallback = function(parentChunkLoadingFunction, data) { var chunkIds = data[0]; var moreModules = data[1]; var runtime = data[2]; var moduleId, chunkId, i = 0; if (chunkIds.some(function(id) { return installedChunks[id] !== 0; })) { for (moduleId in moreModules) { if (__webpack_require__.o(moreModules, moduleId)) { __webpack_require__.m[moduleId] = moreModules[moduleId]; } } if (runtime) { var result = runtime(__webpack_require__); } } if (parentChunkLoadingFunction) { parentChunkLoadingFunction(data); } for (; i < chunkIds.length; i++) { chunkId = chunkIds[i]; if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { installedChunks[chunkId][0](); } installedChunks[chunkId] = 0; } return __webpack_require__.O(result); } var chunkLoadingGlobal = self["webpackChunkmobx"] = self["webpackChunkmobx"] || []; chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
|
Optimize.RuntimeChunk配置的作用
将运行时模块管理相关的代码从app.js中抽离出来,有利于缓存。因为线上环境vendors文件名都会带有hash,假如vendors内容改了导致hash改了,app.js也得做变更,影响网页性能。
1 2 3 4
| var __webpack_exports__ = __webpack_require__.O(undefined, ["vendors"], function() { return __webpack_require__("./client/src/entry-client.tsx"); })
|