小編給大家分享一下webpack實現(xiàn)模塊化加載的方法是什么,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷、網(wǎng)站重做改版、西林網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5高端網(wǎng)站建設(shè)、商城網(wǎng)站定制開發(fā)、集團公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為西林等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
webpack支持的模塊規(guī)范有 AMD 、CommonJS、ES2015 import 等規(guī)范。不管何種規(guī)范大致可以分為同步加載和異步加載兩種情況。本文將介紹webpack是如何實現(xiàn)模塊管理和加載。
同步加載如下:
import a from './a' console.log(a);
異步加載如下:
import('./a').then(a => console.log(a));
webpacks實現(xiàn)的啟動函數(shù),直接將入口程序module傳入啟動函數(shù)內(nèi),并緩存在閉包內(nèi),如下:
(function(modules){ ...... // 加載入口模塊并導(dǎo)出(實現(xiàn)啟動程序) 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在實現(xiàn)模塊管理上不管服務(wù)端還是客戶端大致是一樣,主要由installedChunks記錄已經(jīng)加載的chunk,installedModules記錄已經(jīng)執(zhí)行過的模塊,具體如下:
/** * module 緩存器 * key 為 moduleId (一般為文件路徑) * value 為 module 對象 {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ù)端還是客戶端同步加載的方法都一樣,主要是檢測installedModules中是否已經(jīng)緩存有要加載的module,有則直接返回,否則就創(chuàng)建一個新的module,并執(zhí)行返回module.exports,具體實現(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)建一個新的 module, 并添加到緩存中 var module = installedModules[moduleId] = { i: moduleId, l: false, // 是否已經(jīng)加載 exports: {} // 暴露的對象 }; // 執(zhí)行當(dāng)前 module 的方法 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 標(biāo)記 module 加載完成狀態(tài) module.l = true; // 返回 module 暴露的 exports 對象 return module.exports; }
服務(wù)端的異步加載是通過node的require方法加載chunk并返回一個promises對象。所有chunk都是暴露出ids和modules對象,具體實現(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原理進行加載資源,將chunk內(nèi)容([chunkIds, modules])存到全局的webpackJsonp數(shù)組中,并改造webpackJsonp的push方法實現(xiàn)監(jiān)聽chunk加載完成事件。具體實現(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í)行的隊列中 } installedChunks[chunkId] = 0; // 將chunk標(biāo)記為加載完成 } // 將本次加載回來的 module 添加到全局的 modules 對象 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)聽方法的實現(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 時表示已經(jīng)安裝完成 if(installedChunkData) { // 加載中 promises.push(installedChunkData[2]); } else { // 創(chuàng)建一個回調(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)閉超時定時器 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è)置超時定時器 onScriptComplete({ type: 'timeout', target: script }); }, 120000); script.onerror = script.onload = onScriptComplete; // 設(shè)置加載完成回調(diào) document.head.appendChild(script); } } return Promise.all(promises); };
看完了這篇文章,相信你對“webpack實現(xiàn)模塊化加載的方法是什么”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!