webpack联邦模块之remotes方法
使用聯(lián)邦模塊后當前項目就會有兩個依賴,一個是依賴共享模塊,一個是依賴遠程模塊。運行時webpack/runtime/consumes用于解析共享模塊,運行時webpack/runtime/remotes 用于解析遠程模塊。這兩個模塊對應的方法分別是__webpack_require__.f.consumes ****和__webpack_require__.f.remotes,是的這兩個方法都是關(guān)在__webpack_require__.f上的,和上一篇文章中的__webpack_require__.f.j在一起,都是用于加載chunk中的一環(huán)。
webpack/runtime/remotes
var chunkMapping = {"src_bootstrap_js": ["webpack/container/remote/app2/App","webpack/container/remote/app2/uitls"] };var idToExternalAndNameMapping = {"webpack/container/remote/app2/App": ["default","./App","webpack/container/reference/app2"],"webpack/container/remote/app2/uitls": ["default","./uitls","webpack/container/reference/app2"] };__webpack_require__.f.remotes = (chunkId, promises) => {... }其中數(shù)據(jù)chunkMapping使用chunkId作為key,value是module組成的數(shù)組。表示該chunk依賴這些模塊。
數(shù)據(jù)idToExternalAndNameMapping則使用module作為key,value是如何加載這個遠程模塊。
[”default”, “./App”, “webpack/container/reference/app2”] 表示"webpack/container/remote/app2/App"模塊可以從”webpack/container/reference/app2”模塊的”./App”獲取到,并且該遠程模塊依賴共享作用域”default”。
一般下劃線分割的字符串是chunkId,斜線分割的是moduleId
在這里__webpack_require__.f.remotes方法做的事情就是當加載chunk src_bootstrap_js時解析該chunk依賴的遠程模塊"webpack/container/remote/app2/App"和"webpack/container/remote/app2/uitls",并進行前置加載。加載完成后將這兩個模塊安裝在當前webpack環(huán)境的__webpack_require__.modules上讓代碼可以通過__webpack_require__(’webpack/container/remote/app2/App’)獲取到對應的遠程模塊導出。
**webpack_require**.f.remotes
__webpack_require__.f.remotes = (chunkId, promises) => {if(__webpack_require__.o(chunkMapping, chunkId)) {chunkMapping[chunkId].forEach((id) => {// getScope是干啥的,不知道干啥的,是不是防止有多個入口(entry),因為現(xiàn)在的例子是單入口的// 要把自己也分享出去才能更加復雜var getScope = __webpack_require__.R;if(!getScope) getScope = [];// data是 idToExternalAndNameMapping 的值var data = idToExternalAndNameMapping[id];if(getScope.indexOf(data) >= 0) return;getScope.push(data);if(data.p) return promises.push(data.p);var onError = (error) => {...}// 為什么要識別出是first?var handleFunction = (fn, arg1, arg2, d, next, first) => {...}var onExternal = (external, _, first) => {...}var onInitialized = (_, external, first) => ...var onFactory = (factory) => {...}handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);}} }就像上面介紹的該方法用于加載入?yún)hunkId依賴的遠程模塊。其核心方法是handleFunction
webpack_require.R的作用是啥?拿到是防止同一個執(zhí)行兩邊?例如index.js中的bootstrap還有別的依賴。這樣就會初始化兩邊了
handleFunction
var handleFunction = (fn, arg1, arg2, d, next, first) => {try {var promise = fn(arg1, arg2);if(promise && promise.then) {var p = promise.then((result) => (next(result, d)), onError);if(first) promises.push(data.p = p); else return p;} else {return next(promise, d, first);}} catch(error) {onError(error);} }代碼行數(shù)不多,想表達的是將arg1和arg2作為入?yún)⒄{(diào)用函數(shù)fn,并將函數(shù)的返回值和參數(shù)d和first一起作為函數(shù)next的入?yún)?#xff0c;并且兼容fn返回值為promise和非promise的情況。如果first為true,并且其返回值是promise,將其push到promises數(shù)組中。promises數(shù)組表示chunk bootstrap_js的加載完成依賴promises數(shù)組全部完成。
為什么只有first才會將promise push到promises中?
所以簡單來講handleFunction就是等fn執(zhí)行完將結(jié)果作為next入?yún)⒗^續(xù)執(zhí)行,起到了拼接兩個函數(shù)順序執(zhí)行的作用。
handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);
__webpack_require__(data[2]) (data[2]為"webpack/container/reference/app2" )執(zhí)行完成后得到結(jié)果作為onExternal的入?yún)?zhí)行該方法。
__webpack_modules__ = {"webpack/container/reference/app2": ((module, __unused_webpack_exports, __webpack_require__) => {"use strict"; var __webpack_error__ = new Error(); module.exports = new Promise((resolve, reject) => {if(typeof app2 !== "undefined") return resolve();__webpack_require__.l(app2Url + "/remoteEntry.js", (event) => {if(typeof app2 !== "undefined") return resolve();var errorType = event && (event.type === 'load' ? 'missing' : event.type);var realSrc = event && event.target && event.target.src;__webpack_error__.message = 'Loading script failed.\n(' + errorType + ': ' + realSrc + ')';__webpack_error__.name = 'ScriptExternalLoadError';__webpack_error__.type = errorType;__webpack_error__.request = realSrc;reject(__webpack_error__);}, "app2"); }).then(() => (app2));}模塊"webpack/container/reference/app2"加載完成后會得到一個對象{get: () => get, init: () => init} 這個對象就是onExternal方法的第一個入?yún)xternal。
onExternal
var onExternal = (external, _, first) => (external ? handleFunction(__webpack_require__.I, data[0], 0, external, onInitialized, first) : onError());一行代碼,表達的是初始化遠程模塊依賴的共享作用域之后執(zhí)行方法onInitialized。
onInitialized
var onInitialized = (_, external, first) => (handleFunction(external.get, data[1]/* ./App */, getScope, 0, onFactory, first));// external.get 方法也就是三方模塊的get方法 var get = (module, getScope) => {__webpack_require__.R = getScope;getScope = (__webpack_require__.o(moduleMap, module)? moduleMap[module](): Promise.resolve().then(() => {throw new Error('Module "' + module + '" does not exist in container.');}));__webpack_require__.R = undefined;return getScope; };var moduleMap = {"./App": () => {return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_react_react-_5e40"),__webpack_require__.e("src_App_js")]).then(() => (/* external.get */**() => ((__webpack_require__(/*! ./src/App */ "./src/App.js")))**));},}通過三方模塊的get方法獲取對應模塊,例如這里的模塊 ./App 得到返回值 () => ((__webpack_require__(/*! ./src/App */ "./App.js"))) 也就是下面的入?yún)actory。
最后執(zhí)行方法onFactory
var onFactory = (factory) => {data.p = 1;// 這里可以通過id拿到遠程庫中的模塊,將id對應的module設(shè)置上__webpack_modules__[id] = (module) => {module.exports = factory();} };到這里__webpack_modules__[’webpack/container/remote/app2/App’] = (module) => {module.exports = factory()} 這樣三方模塊安裝完畢,可以保證chunk src_bootstrap_js 依賴的三方模塊可以通過__webpack_require__正常獲取。
__webpack_require__.I 用于初始化共享作用域當前環(huán)境和remote
該方法不僅僅會初始化自身的共享作用域,還會初始化當前項目依賴的外部項目的共享作用域:
需要注意的是__webpack_require__.I 中初始化外部作用域,并不是通過入?yún)碇酪跏蓟男┩獠孔饔糜?#xff0c;而是直接編譯到源碼中。
initExternal("webpack/container/reference/app2"); // 這樣就可以通過id拿到extrnal調(diào)用使用__webpack_require__.S調(diào)用它的init方法,完成其共享作用域的初始化 __webpack_require__.S = {}; var initPromises = {}; var initTokens = {};__webpack_require__.I = (name, initScope) => {if(!initScope) initScope = [];var initToken = initTokens[name];if(!initToken) initToken = initTokens[name] = {};if(initScope.indexOf(initToken) >= 0) return;initScope.push(initToken);// only runs onceif(initPromises[name]) return initPromises[name];// creates a new share scope if neededif(!__webpack_require__.o(__webpack_require__.S, name)) __webpack_require__.S[name] = {};// runs all init snippets from all modules reachablevar scope = __webpack_require__.S[name];var warn = (msg) => ...var uniqueName = "app1";var register = (name, version, factory, eager) => {...}var initExternal = (id) => {...}var promises = [];switch(name) {case "default": {register("react-dom", "17.0.2", () => (Promise.all([]).then(() => () => {} /* factory */))));register("react", "17.0.2", () => (Promise.all([]).then(() => () /* factory */)));initExternal("webpack/container/reference/app2");}break;} if(!promises.length) return initPromises[name] = 1; return initPromises[name] = Promise.all(promises).then(() => (initPromises[name] = 1));這個方法用于初始化共享作用域。在初始化當前環(huán)境共享作用域后使用方法initExternal("webpack/container/reference/app2");初始化遠程模塊的共享作用域。
var initPromises = {}; var initTokens = {};__webpack_require__.I = (name, initScope) => {if(!initScope) initScope = [];var initToken = initTokens[name];if(!initToken) initToken = initTokens[name] = {};if(initScope.indexOf(initToken) >= 0) return;initScope.push(initToken);... }var initExternal = (id) => {...module.init(__webpack_require__.S[name], initScope)... }這幾行代碼不太好理解。
首先每個作用域都有一個唯一的token與之對應,在這里表現(xiàn)為對象。
正在初始化的作用域列表存在initScope中,如果initScope中存在這個token直接退出,表示正在初始化。(這種情況會出現(xiàn)在相互依賴的情況下,a 依賴 b 初始化b的共享作用域,這時候b也依賴a,b也會初始化a的作用域,這時候傳入的initScope就有代表a作用域的token,否則會一直初始化下去。)
initExternal("webpack/container/reference/app2");
var initExternal = (id) => {var handleError = (err) => (warn("Initialization of sharing external failed: " + err));try {var module = __webpack_require__(id);if(!module) return;var initFn = (module) => (module && module.init && module.init(__webpack_require__.S[name], initScope))if(module.then) return promises.push(module.then(initFn, handleError));var initResult = initFn(module);if(initResult && initResult.then) return promises.push(initResult['catch'](handleError));} catch(err) { handleError(err); } }這段代碼表達的是,等待遠程模塊加載完成拿到其init方法,使用當前環(huán)境的共享作用域__webpack_require__.S的某個這里是default來初始化遠程環(huán)境共享作用域。
register("react", "17.0.2", () => (Promise...
var register = (name, version, factory, eager) => {var versions = scope[name] = scope[name] || {};var activeVersion = versions[version];if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager }; };向指定的共享作用域上設(shè)置包。
共享作用域初始化完成后當前環(huán)境的共享作用域形狀如下:
__webpack_require__.S.default = {react: {'17.0.2': {get: () => ..., from: uniqueName, eager: boolean}},... }到這里加載chunk src_bootstrap_js 依賴的遠程模塊準備完畢,并且遠程模塊依賴的共享作用域同樣初始化完畢。但是src_bootstrap_js還沒有加載完畢,因為雖然遠程模塊可以通過__webpack_require__方法直接獲取到,但是共享模塊還無法通過該方法直接獲取,因為到目前為止,共享模塊只存在于__webpack_require__.S.default 上,而不存在于__webpack_modules__上。要將__webpack_require__.S.default上的共享模塊在__webpack_modules__上獲取還要借助接下來的方法__webpack_require__.f.consumes = (chunkId, promises) => {...}的幫助。
總結(jié)
webpack_require.f.remotes使用chunkId和promises作為入?yún)?#xff0c;解析chunkId依賴的遠程模塊并加載和安裝,讓被依賴的遠程模塊可以通過__webpack_require__方法直接獲取。
安裝遠程依賴的過程包括下載遠程依賴和初始化遠程依賴依賴的共享作用域,完善遠程依賴的執(zhí)行環(huán)境。
總結(jié)
以上是生活随笔為你收集整理的webpack联邦模块之remotes方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: webpack联邦模块之webpack运
- 下一篇: webpack联邦模块之consumes