溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

webpack是如何實(shí)現(xiàn)模塊化加載的

發(fā)布時(shí)間:2020-05-18 03:40:45 來源:網(wǎng)絡(luò) 閱讀:1173 作者:dolunbu 欄目:web開發(fā)

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)懶加載?

?


向AI問一下細(xì)節(jié)

免責(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)容。

AI