您好,登錄后才能下訂單哦!
webpack支持的模塊規(guī)范有?AMD?、CommonJS、ES2015 import?等規(guī)范。不管何種規(guī)范大致可以分為同步加載和異步加載兩種情況。本文將介紹webpack是如何實(shí)現(xiàn)模塊管理和加載。
同步加載如下:
import?a?from?'./a'; console.log(a);
異步加載如下:
import('./a').then(a?=>?console.log(a));
webpacks實(shí)現(xiàn)的啟動(dòng)函數(shù),直接將入口程序module傳入啟動(dòng)函數(shù)內(nèi),并緩存在閉包內(nèi),如下:
(function(modules){ ...... //?加載入口模塊并導(dǎo)出(實(shí)現(xiàn)啟動(dòng)程序) return?__webpack_require__(__webpack_require__.s?=?0); })({ 0:?(function(module,?__webpack_exports__,?__webpack_require__)?{ module.exports?=?__webpack_require__(/*!?./src/app.js?*/"./src/app.js"); }) })
webpack在實(shí)現(xiàn)模塊管理上不管服務(wù)端還是客戶端大致是一樣,主要由installedChunks記錄已經(jīng)加載的chunk,installedModules記錄已經(jīng)執(zhí)行過的模塊,具體如下:
/** *?module?緩存器 *?key?為?moduleId?(一般為文件路徑) *?value?為?module?對(duì)象?{i:?moduleId,?l:?false,?exports:?{}} */ var?installedModules?=?{}; /** *?chunks加載狀態(tài)記錄器 *?key?一般為?chunk?索引 *?value?undefined:未加載?0:已經(jīng)加載?(客戶端特有?null:?準(zhǔn)備加載?[resolve,?reject]:?加載中) */ var?installedChunks?=?{ "app":?0 }
不管是服務(wù)端還是客戶端同步加載的方法都一樣,主要是檢測(cè)installedModules中是否已經(jīng)緩存有要加載的module,有則直接返回,否則就創(chuàng)建一個(gè)新的module,并執(zhí)行返回module.exports,具體實(shí)現(xiàn)如下:
//?編譯后的同步加載 __webpack_require__(/*!?./src/app.js?*/"./src/app.js"); //?加載模塊的方法,即require方法 function?__webpack_require__(moduleId)?{ //?檢查當(dāng)前的module是否已經(jīng)存在緩存中 if(installedModules[moduleId])?{ return?installedModules[moduleId].exports;?//?直接返回已緩存的?module.exports } //?創(chuàng)建一個(gè)新的?module,?并添加到緩存中 var?module?=?installedModules[moduleId]?=?{ i:?moduleId, l:?false,?//?是否已經(jīng)加載 exports:?{}?//?暴露的對(duì)象 }; //?執(zhí)行當(dāng)前?module?的方法 modules[moduleId].call(module.exports,?module,?module.exports,?__webpack_require__); //?標(biāo)記?module?加載完成狀態(tài) module.l?=?true; //?返回?module?暴露的?exports?對(duì)象 return?module.exports; }
服務(wù)端的異步加載是通過node的require方法加載chunk并返回一個(gè)promises對(duì)象。所有chunk都是暴露出ids和modules對(duì)象,具體實(shí)現(xiàn)如下:
//?編譯后的異步加載方法 __webpack_require__.e(/*!?import()?*/?0).then(__webpack_require__.bind(null,?/*!?./c.js?*/?"./src/c.js")) //?chunk?0?代碼如下(即0.js的代碼) exports.ids?=?[0]; exports.modules?=?{ "./src/c.js":?(function(module,?__webpack_exports__,?__webpack_require__)?{ "use?strict"; __webpack_require__.r(__webpack_exports__); __webpack_exports__["default"]?=?(function?()?{ console.log('c'); }) }) } //?異步加載模塊方法 __webpack_require__.e?=?function?requireEnsure(chunkId)?{ var?promises?=?[]; if(installedChunks[chunkId]?!==?0)?{ var?chunk?=?require("./"?+?({}[chunkId]||chunkId)?+?".js"); var?moreModules?=?chunk.modules,?chunkIds?=?chunk.ids; for(var?moduleId?in?moreModules)?{ modules[moduleId]?=?moreModules[moduleId]; } for(var?i?=?0;?i?<?chunkIds.length;?i++) installedChunks[chunkIds[i]]?=?0; } return?Promise.all(promises); }
客戶端的異步加載是通過JSONP原理進(jìn)行加載資源,將chunk內(nèi)容([chunkIds, modules])存到全局的webpackJsonp數(shù)組中,并改造webpackJsonp的push方法實(shí)現(xiàn)監(jiān)聽chunk加載完成事件。具體實(shí)現(xiàn)如下:
//?編譯后的異步加載方法 __webpack_require__.e(/*!?import()?*/?0).then(__webpack_require__.bind(null,?/*!?./c.js?*/?"./src/c.js")) //?chunk?0?代碼如下(即0.js的代碼) (window["webpackJsonp"]?=?window["webpackJsonp"]?||?[]).push([[0],{ "./src/c.js":?(function(module,?__webpack_exports__,?__webpack_require__)?{ "use?strict"; __webpack_require__.r(__webpack_exports__); __webpack_exports__["default"]?=?(function?()?{ console.log('c'); }); }) }]); //?加載成功的回調(diào)函數(shù) function?webpackJsonpCallback(data)?{ var?chunkIds?=?data[0]; var?moreModules?=?data[1]; //?將本次加載回來的?chunk?標(biāo)記為加載完成狀態(tài),并執(zhí)行回調(diào) var?moduleId,?chunkId,?i?=?0,?resolves?=?[]; for(;i?<?chunkIds.length;?i++)?{ chunkId?=?chunkIds[i]; if(Object.prototype.hasOwnProperty.call(installedChunks,?chunkId)?&&?installedChunks[chunkId])?{ resolves.push(installedChunks[chunkId][0]);?//?將chunk成功回調(diào)添加到要執(zhí)行的隊(duì)列中 } installedChunks[chunkId]?=?0;?//?將chunk標(biāo)記為加載完成 } //?將本次加載回來的?module?添加到全局的?modules?對(duì)象 for(moduleId?in?moreModules)?{ if(Object.prototype.hasOwnProperty.call(moreModules,?moduleId))?{ modules[moduleId]?=?moreModules[moduleId]; } } //?判斷?webpackJsonp?數(shù)組原始的push方法是否存在,存在則將數(shù)據(jù)追加到webpackJsonp中 if(parentJsonpFunction)?parentJsonpFunction(data); //?執(zhí)行所有?chunk?回調(diào) while(resolves.length)?{ resolves.shift()(); } }; //?加載完成監(jiān)聽方法的實(shí)現(xiàn) var?jsonpArray?=?window["webpackJsonp"]?=?window["webpackJsonp"]?||?[]; var?oldJsonpFunction?=?jsonpArray.push.bind(jsonpArray); jsonpArray.push?=?webpackJsonpCallback; jsonpArray?=?jsonpArray.slice(); for(var?i?=?0;?i?<?jsonpArray.length;?i++)?webpackJsonpCallback(jsonpArray[i]); var?parentJsonpFunction?=?oldJsonpFunction; //?異步加載模塊方法 __webpack_require__.e?=?function?requireEnsure(chunkId)?{ var?promises?=?[]; var?installedChunkData?=?installedChunks[chunkId]; if(installedChunkData?!==?0)?{?//?0?時(shí)表示已經(jīng)安裝完成 if(installedChunkData)?{?//?加載中 promises.push(installedChunkData[2]); }?else?{ //?創(chuàng)建一個(gè)回調(diào)的Promise,并將Promise緩存到installedChunks中 var?promise?=?new?Promise(function(resolve,?reject)?{ installedChunkData?=?installedChunks[chunkId]?=?[resolve,?reject]; }); promises.push(installedChunkData[2]?=?promise); var?script?=?document.createElement('script'); var?onScriptComplete; script.charset?=?'utf-8'; script.timeout?=?120; if?(__webpack_require__.nc)?{ script.setAttribute("nonce",?__webpack_require__.nc); } script.src?=?jsonpScriptSrc(chunkId); var?error?=?new?Error(); onScriptComplete?=?function?(event)?{?//?加載完成回調(diào) //?避免IE內(nèi)存泄漏。 script.onerror?=?script.onload?=?null; clearTimeout(timeout);?//?關(guān)閉超時(shí)定時(shí)器 var?chunk?=?installedChunks[chunkId]; if(chunk?!==?0)?{?//?未加載完成 if(chunk)?{?//?加載中 var?errorType?=?event?&&?(event.type?===?'load'???'missing'?:?event.type); var?realSrc?=?event?&&?event.target?&&?event.target.src; error.message?=?'Loading?chunk?'?+?chunkId?+?'?failed.\n('?+?errorType?+?':?'?+?realSrc?+?')'; error.name?=?'ChunkLoadError'; error.type?=?errorType; error.request?=?realSrc; chunk[1](error); } installedChunks[chunkId]?=?undefined; } }; var?timeout?=?setTimeout(function(){?//?設(shè)置超時(shí)定時(shí)器 onScriptComplete({?type:?'timeout',?target:?script?}); },?120000); script.onerror?=?script.onload?=?onScriptComplete;?//?設(shè)置加載完成回調(diào) document.head.appendChild(script); } } return?Promise.all(promises); };
文章同步發(fā)布:?https://www.geek-share.com/detail/2784034803.html
參考文章:
詳解webpack + react + react-router 如何實(shí)現(xiàn)懶加載
Webpack2 + Vue2 + Vue-Router2 如何實(shí)現(xiàn)懶加載?
?
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。