詳解webpack2異步加載套路
webpack提供的一個(gè)非常強(qiáng)大的功能就是code spliting(代碼切割)。
在webpack 1.x中提供了
require.ensure([], () => { let module = require('./page1/module'); // do something }, 'module1')
利用require.ensure這個(gè)API使得webpack單獨(dú)將這個(gè)文件打包成一個(gè)可以異步加載的chunk.
具體的套路見我寫的另一篇blog: webpack分包及異步加載套路
一句話總結(jié)就是:
在輸出的runtime代碼中,包含了異步chunk的id及chunk name的映射關(guān)系。需要異步加載相應(yīng)的chunk時(shí),通過(guò)生成script標(biāo)簽,然后插入到DOM中完成chunk的加載。通過(guò)JSONP,runtime中定義好函數(shù),chunk加載完成后即會(huì)立即執(zhí)行這個(gè)函數(shù)。
從編譯生成后的代碼來(lái)看,webpack 1.x從chunk的加載到執(zhí)行的過(guò)程處理的比較粗糙,僅僅是通過(guò)添加script標(biāo)簽,異步加載chunk后,完成函數(shù)的執(zhí)行。
這個(gè)過(guò)程當(dāng)中,如果出現(xiàn)了chunk加載不成功時(shí),這種情況下應(yīng)該如何去容錯(cuò)呢?
在webpack2中相比于webpack1.x在這個(gè)點(diǎn)的處理上是將chunk的加載包裹在了promise當(dāng)中,那么這個(gè)過(guò)程變的可控起來(lái)。具體的webpack2實(shí)現(xiàn)套路也是本文想要去說(shuō)明的地方。
webpack提供的異步加載函數(shù)是
/******/ // This file contains only the entry chunk. /******/ // The chunk loading function for additional chunks // runtime代碼里面只包含了入口的chunk // 這個(gè)函數(shù)的主要作用: // 1. 異步加載chunk // 2. 提供對(duì)于chunk加載失敗或者處于加載中的處理 // 其中chunk加載狀態(tài)的判斷是根據(jù)installedChunks對(duì)象chunkId是數(shù)字0還是數(shù)組來(lái)進(jìn)行判斷的 /******/ __webpack_require__.e = function requireEnsure(chunkId) { // 數(shù)字0代表chunk加載成功 /******/ if(installedChunks[chunkId] === 0) /******/ return Promise.resolve(); /******/ // an Promise means "currently loading". // 如果installedChunks[chunkId]為一個(gè)數(shù)組 /******/ if(installedChunks[chunkId]) { // 返回一個(gè)promise對(duì)象 /******/ return installedChunks[chunkId][2]; /******/ } /******/ // start chunk loading // 通過(guò)生成script標(biāo)簽來(lái)異步加載chunk.文件名是根據(jù)接受的chunkId來(lái)確認(rèn)的 /******/ var head = document.getElementsByTagName('head')[0]; /******/ var script = document.createElement('script'); /******/ script.type = 'text/javascript'; /******/ script.charset = 'utf-8'; /******/ script.async = true; // 超時(shí)時(shí)間為120s /******/ script.timeout = 120000; /******/ if (__webpack_require__.nc) { /******/ script.setAttribute("nonce", __webpack_require__.nc); /******/ } // 需要加載的文件名 /******/ script.src = __webpack_require__.p + "js/register/" + ({"2":"index"}[chunkId]||chunkId) + ".js"; // 120s的定時(shí)器,超時(shí)后觸發(fā)onScriptComplete回調(diào) /******/ var timeout = setTimeout(onScriptComplete, 120000); // chunk加載完畢后的回調(diào) /******/ script.onerror = script.onload = onScriptComplete; /******/ function onScriptComplete() { /******/ // avoid mem leaks in IE. /******/ script.onerror = script.onload = null; // 清空定時(shí)器 /******/ clearTimeout(timeout); // 獲取這個(gè)chunk的加載狀態(tài) // 若為數(shù)字0,表示加載成功 // 若為一個(gè)數(shù)組, 調(diào)用數(shù)組的第2個(gè)元素(第二個(gè)元素為promise內(nèi)傳入的reject函數(shù)),使得promise捕獲拋出的錯(cuò)誤。reject(new Error('xxx')) /******/ var chunk = installedChunks[chunkId]; /******/ if(chunk !== 0) { /******/ if(chunk) chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); /******/ installedChunks[chunkId] = undefined; /******/ } /******/ }; // 每次需要進(jìn)行異步加載chunk時(shí),會(huì)將這個(gè)chunk的加載狀態(tài)進(jìn)行初始化為一個(gè)數(shù)組,并以key/value的形式保存在installedChunks里 // 這個(gè)數(shù)組為[resolve, reject, promise]; /******/ var promise = new Promise(function(resolve, reject) { /******/ installedChunks[chunkId] = [resolve, reject]; /******/ }); /******/ installedChunks[chunkId][2] = promise; /******/ head.appendChild(script); //返回promise /******/ return promise; /******/ };
我們?cè)賮?lái)看看路由配置文件編譯后生成的代碼index.js, 特別注意下__webpack_require__.e這個(gè)異步加載函數(shù):
Router .home('path1') .addRoute({ path: 'path1', animate: 'zoomIn', viewBox: '.public-path1-container', template: __webpack_require__(5), // 掛載controller pageInit: function pageInit() { var _this = this; console.time('route async path1'); // 異步加載0.js(這個(gè)文件是webpack通過(guò)code spliting自己生成的文件名) // 具體異步加載代碼的封裝見?分析 // 其中0.js包含了包含了path1這個(gè)路由下的業(yè)務(wù)代碼 // __webpack_require__.e(0) 起的作用僅為加載chunk以及提供對(duì)于chunk加載失敗錯(cuò)誤的拋出 // 具體的業(yè)務(wù)代碼的觸發(fā)是通過(guò)__webpack_require_e(0).then(__webpack_require__.bind(null, 8)).then(function(module) { ... })進(jìn)行觸發(fā) // __webpack_require__.bind(null, 8) 返回的是module[8]暴露出來(lái)的module // 這段代碼執(zhí)行時(shí),首先初始化一個(gè)module對(duì)象 // module = { // i: moduleId, // 模塊id // l: false, // 加載狀態(tài) // exports: {} // 需要暴露的對(duì)象 // } // 通過(guò)異步加載的chunk最后暴露出來(lái)的對(duì)象是作為了module.exports.default屬性 // 因此在第二個(gè)方法中傳入的對(duì)象的default屬性才是你模塊8真正所暴露的對(duì)象 __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 8)).then(function (module) { var controller = module.default; Router.registerCtrl('path1', new controller(_this.viewBox)); // 添加錯(cuò)誤處理函數(shù),用以捕獲前面可能拋出的錯(cuò)誤 }).catch(function (e) { return console.log('chunk loading failed'); }); }, // 進(jìn)入路由跳轉(zhuǎn)之前 beforeEnter: function beforeEnter() {}, // 路由跳轉(zhuǎn)前 beforeLeave: function beforeLeave() {} }) .addRoute({ path: 'path2', viewBox: '.public-path2-container', animate: 'zoomIn', template: __webpack_require__(6), pageInit: function pageInit() { var _this2 = this; __webpack_require__.e/* import() */(1).then(__webpack_require__.bind(null, 9)).then(function (module) { console.time('route async path2'); var controller = module.default; Router.registerCtrl('path2', new controller(_this2.viewBox)); }).catch(function (e) { return console.log('chunk loading failed'); }); }, beforeEnter: function beforeEnter() {}, beforeLeave: function beforeLeave() {} }); Router.bootstrap();
總結(jié)一下就是:
webpack2相比于webpack1.x將異步加載chunk的過(guò)程封裝在了promise當(dāng)中,如果chunk加載超時(shí)或者失敗會(huì)拋出錯(cuò)誤,這時(shí)我們可以針對(duì)拋出的錯(cuò)誤做相應(yīng)的錯(cuò)誤處理。
此外還應(yīng)該注意下,webpack2異步加載chunk是基于原生的promise。如果部分環(huán)境暫時(shí)還不支持原生promise時(shí)需要提供polyfill。另外就是require.ensure可以接受第三個(gè)參數(shù)用以給chunk命名,但是import這個(gè)API沒有提供這個(gè)方法
更多的細(xì)節(jié)大家可以運(yùn)行demo看下編譯后的代碼
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
僅在IE6/7/8下cssText返回值少了分號(hào)的測(cè)試代碼
在IE6/7/8中少了分號(hào)。使用cssText屬性時(shí)需注意。2011-03-03獲取頁(yè)面高度,窗口高度,滾動(dòng)條高度等參數(shù)值getPageSize,getPageScroll
獲取頁(yè)面高度,窗口高度,滾動(dòng)條高度等參數(shù)值getPageSize,getPageScroll...2006-09-09一個(gè)字符串中出現(xiàn)次數(shù)最多的字符 統(tǒng)計(jì)這個(gè)次數(shù)【實(shí)現(xiàn)代碼】
下面小編就為大家?guī)?lái)一篇一個(gè)字符串中出現(xiàn)次數(shù)最多的字符 統(tǒng)計(jì)這個(gè)次數(shù)【實(shí)現(xiàn)代碼】。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考2016-04-04