本文是《彻底搞懂 Module Federation》系列的第4篇,深入分析 Module Federation 模块加载的执行流程。
📚 系列文章
- 📖 第1篇:概述与实战示例
- 📖 第2篇:原理分析 - Webpack 异步加载流程
- 📖 第3篇:原理分析 - MF 模块加载(上)
- ✅ 第4篇(本文):原理分析 - MF 模块加载(下)
- 📖 第5篇:原理分析 - Runtime API + 项目实操
多了__webpack_require__.f.remotes 和__webpack_require__.f.resumes方法,
markdown
__webpack_require__.f.resumes
__webpack_require__.f.remotes
__webpack_require__.f.MiniCss
__webpack_require__.f.l
3.2.4.2 webpack_require.f.consumes
css
var parseVersion = (str) => {
// see webpack/lib/util/semver.js for original code
var p=p=>{return p.split(".").map((p=>{return+p==p?+p:p}))},n=/^([^-+]+)?(?:-([^+]+))?(?:+(.+))?$/.exec(str),r=n[1]?p(n[1]):[];return n[2]&&(r.length++,r.push.apply(r,p(n[2]))),n[3]&&(r.push([]),r.push.apply(r,p(n[3]))),r;
}
var versionLt = (a, b) => {
// see webpack/lib/util/semver.js for original code
a=parseVersion(a),b=parseVersion(b);for(var r=0;;){if(r>=a.length)return r<b.length&&"u"!=(typeof b[r])[0];var e=a[r],n=(typeof e)[0];if(r>=b.length)return"u"==n;var t=b[r],f=(typeof t)[0];if(n!=f)return"o"==n&&"n"==f||("s"==f||"u"==n);if("o"!=n&&"u"!=n&&e!=t)return e<t;r++}
}
var rangeToString = (range) => {
// see webpack/lib/util/semver.js for original code
var r=range[0],n="";if(1===range.length)return"*";if(r+.5){n+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var e=1,a=1;a<range.length;a++){e--,n+="u"==(typeof(t=range[a]))[0]?"-":(e>0?".":"")+(e=2,t)}return n}var g=[];for(a=1;a<range.length;a++){var t=range[a];g.push(0===t?"not("+o()+")":1===t?"("+o()+" || "+o()+")":2===t?g.pop()+" "+g.pop():rangeToString(t))}return o();function o(){return g.pop().replace(/^((.+))$/,"$1")}
}
var satisfy = (range, version) => {
// see webpack/lib/util/semver.js for original code
if(0 in range){version=parseVersion(version);var e=range[0],r=e<0;r&&(e=-e-1);for(var n=0,i=1,a=!0;;i++,n++){var f,s,g=i<range.length?(typeof range[i])[0]:"";if(n>=version.length||"o"==(s=(typeof(f=version[n]))[0]))return!a||("u"==g?i>e&&!r:""==g!=r);if("u"==s){if(!a||"u"!=g)return!1}else if(a)if(g==s)if(i<=e){if(f!=range[i])return!1}else{if(r?f>range[i]:f<range[i])return!1;f!=range[i]&&(a=!1)}else if("s"!=g&&"n"!=g){if(r||i<=e)return!1;a=!1,i--}else{if(i<=e||s<g!=r)return!1;a=!1}else"s"!=g&&"n"!=g&&(a=!1,i--)}}var t=[],o=t.pop.bind(t);for(n=1;n<range.length;n++){var u=range[n];t.push(1==u?o()|o():2==u?o()&o():u?satisfy(u,version):!o())}return!!o();
}
var exists = (scope, key) => {
return scope && __webpack_require__.o(scope, key);
}
var get = (entry) => {
entry.loaded = 1;
return entry.get()
};
var eagerOnly = (versions) => {
return Object.keys(versions).reduce((filtered, version) => {
if (versions[version].eager) {
filtered[version] = versions[version];
}
return filtered;
}, {});
};
var findLatestVersion = (scope, key, eager) => {
var versions = eager ? eagerOnly(scope[key]) : scope[key];
var key = Object.keys(versions).reduce((a, b) => {
return !a || versionLt(a, b) ? b : a;
}, 0);
return key && versions[key];
};
var findSatisfyingVersion = (scope, key, requiredVersion, eager) => {
var versions = eager ? eagerOnly(scope[key]) : scope[key];
var key = Object.keys(versions).reduce((a, b) => {
if (!satisfy(requiredVersion, b)) return a;
return !a || versionLt(a, b) ? b : a;
}, 0);
return key && versions[key]
};
var findSingletonVersionKey = (scope, key, eager) => {
var versions = eager ? eagerOnly(scope[key]) : scope[key];
return Object.keys(versions).reduce((a, b) => {
return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a;
}, 0);
};
var getInvalidSingletonVersionMessage = (scope, key, version, requiredVersion) => {
return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")"
};
var getInvalidVersionMessage = (scope, scopeName, key, requiredVersion, eager) => {
var versions = scope[key];
return "No satisfying version (" + rangeToString(requiredVersion) + ")" + (eager ? " for eager consumption" : "") + " of shared module " + key + " found in shared scope " + scopeName + ".\n" +
"Available versions: " + Object.keys(versions).map((key) => {
return key + " from " + versions[key].from;
}).join(", ");
};
var fail = (msg) => {
throw new Error(msg);
}
var failAsNotExist = (scopeName, key) => {
return fail("Shared module " + key + " doesn't exist in shared scope " + scopeName);
}
var warn = /*#__PURE__*/ (msg) => {
if (typeof console !== "undefined" && console.warn) console.warn(msg);
};
var init = (fn) => (function(scopeName, key, eager, c, d) {
var promise = __webpack_require__.I(scopeName);
if (promise && promise.then && !eager) {
return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], key, false, c, d));
}
return fn(scopeName, __webpack_require__.S[scopeName], key, eager, c, d);
});
var useFallback = (scopeName, key, fallback) => {
return fallback ? fallback() : failAsNotExist(scopeName, key);
}
var load = /*#__PURE__*/ init((scopeName, scope, key, eager, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
return get(findLatestVersion(scope, key, eager));
});
var loadVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager);
if (satisfyingVersion) return get(satisfyingVersion);
warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager))
return get(findLatestVersion(scope, key, eager));
});
var loadStrictVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager);
if (satisfyingVersion) return get(satisfyingVersion);
if (fallback) return fallback();
fail(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager));
});
var loadSingleton = /*#__PURE__*/ init((scopeName, scope, key, eager, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var version = findSingletonVersionKey(scope, key, eager);
return get(scope[key][version]);
});
var loadSingletonVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var version = findSingletonVersionKey(scope, key, eager);
if (!satisfy(requiredVersion, version)) {
warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));
}
return get(scope[key][version]);
});
var loadStrictSingletonVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var version = findSingletonVersionKey(scope, key, eager);
if (!satisfy(requiredVersion, version)) {
fail(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));
}
return get(scope[key][version]);
});
var installedModules = {};
var moduleToHandlerMapping = {
"webpack/sharing/consume/default/vue/vue": () => (loadSingletonVersion("default", "vue", false, [1,3,0,11], () => (__webpack_require__.e("vendors-node_modules_pnpm_vue_3_3_7_typescript_5_6_3_node_modules_vue_dist_vue_runtime_esm-bu-3fdf17").then(() => (() => (__webpack_require__(/*! vue */ "../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js")))))))
};
// no consumes in initial chunks
// 模块依赖的共享依赖
var chunkMapping = {
"src_main_js": [
"webpack/sharing/consume/default/vue/vue"
]
};
var startedInstallModules = {};
__webpack_require__.f.consumes = (chunkId, promises) => {
if(__webpack_require__.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
if(__webpack_require__.o(installedModules, id)) return promises.push(installedModules[id]);
if(!startedInstallModules[id]) {
var onFactory = (factory) => {
installedModules[id] = 0;
__webpack_require__.m[id] = (module) => {
delete __webpack_require__.c[id];
module.exports = factory();
}
};
startedInstallModules[id] = true;
var onError = (error) => {
delete installedModules[id];
__webpack_require__.m[id] = (module) => {
delete __webpack_require__.c[id];
throw error;
}
};
try {
var promise = moduleToHandlerMapping[id]();
if(promise.then) {
promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError));
} else onFactory(promise);
} catch(e) { onError(e); }
}
});
}
}
项目里面shared 共享依赖,所以在执行到__webpack_require__.f.consumes开始检查模块是否有对应的共享依赖,这里可以发现就是webpack/sharing/consume/default/vue/vue
接着从moduleToHandlerMapping 对象里面,寻找共享依赖的加载和版本对比逻辑, 通过loadSingletonVersion 函数进行加载,主要我们在配置中设置了singleton: true
javascript
var moduleToHandlerMapping = {
"webpack/sharing/consume/default/vue/vue":
() => (loadSingletonVersion("default", "vue", false, [1,3,0,11], () => (__webpack_require__.e("vendors-node_modules_pnpm_vue_3_3_7_typescript_5_6_3_node_modules_vue_dist_vue_runtime_esm-bu-3fdf17").then(() => (() => (__webpack_require__(/*! vue */ "../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js")))))))
};
scss
var loadSingletonVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => {
if (!exists(scope, key)) return useFallback(scopeName, key, fallback);
var version = findSingletonVersionKey(scope, key, eager);
if (!satisfy(requiredVersion, version)) {
warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion));
}
return get(scope[key][version]);
});
在执行 loadSingletonVersion之前,首先要执行了 init方法,
php
var init = (fn) => (function(scopeName, key, eager, c, d) {
var promise = __webpack_require__.I(scopeName);
if (promise && promise.then && !eager) {
return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], key, false, c, d));
}
return fn(scopeName, __webpack_require__.S[scopeName], key, eager, c, d);
});
继续走到init方法,里面有__webpack_require__.I,接着看I函数,它是一个独立的模块,在runtime/share
3.2.4.3 webpack_require.I
ini
__webpack_require__.S = {};
var initPromises = {};
var initTokens = {};
__webpack_require__.I = (name, initScope) => {
if(!initScope) initScope = [];
// handling circular init calls
var initToken = initTokens[name];
if(!initToken) initToken = initTokens[name] = {};
if(initScope.indexOf(initToken) >= 0) return;
initScope.push(initToken);
// only runs once
if(initPromises[name]) return initPromises[name];
// creates a new share scope if needed
if(!__webpack_require__.o(__webpack_require__.S, name)) __webpack_require__.S[name] = {};
// runs all init snippets from all modules reachable
var scope = __webpack_require__.S[name];
var warn = (msg) => {
if (typeof console !== "undefined" && console.warn) console.warn(msg);
};
var uniqueName = "vue3-demo_layout";
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 };
};
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); }
}
var promises = [];
switch(name) {
case "default": {
register("vue", "3.3.7", () => (__webpack_require__.e("vendors-node_modules_pnpm_vue_3_3_7_typescript_5_6_3_node_modules_vue_dist_vue_runtime_esm-bu-3fdf17").then(() => (() => (__webpack_require__(/*! ../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js */ "../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js"))))));
initExternal("webpack/container/reference/home");
}
break;
}
if(!promises.length) return initPromises[name] = 1;
return initPromises[name] = Promise.all(promises).then(() => (initPromises[name] = 1));
};
执行到register里面,可以看到还是将共享依赖,包括具体版本号,设置到了全局变量__webpack_require__.S上面,数据结构就是
__webpack_require__.S[version] = { get: factory, from: uniqueName, eager: !!eager }
里面包括了依赖的
- 版本号3.3.7,
- from:vue3-demo_layout 来源、
- Get: 获取方法
() => (__webpack_require__.e("vendors-
具体到我们的demo,可以看一下
dart
{
"default": {
"vue": {
"3.3.7": {
"from": "vue3-demo_layout",
"eager": false
// get 包括了具体vue如何请求到
get: () => (__webpack_require__.e("vendors-node_modules_pnpm_vue_3_3_7_typescript_5_6_3_node_modules_vue_dist_vue_runtime_esm-bu-3fdf17").then(() => (() => (__webpack_require__(/*! ../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js */ "../../node_modules/.pnpm/vue@3.3.7_typescript@5.6.3/node_modules/vue/dist/vue.runtime.esm-bundler.js")))))
}
}
}
}
__webpack_require__ .S
register后,接着就是加载远程应用
__webpack_require__(webpack/container/reference/home),这个模块在前面已经注册过了
javascript
{
/***/ "webpack/container/reference/home":
/*!************************************************************!*\
!*** external "home@http://localhost:3002/remoteEntry.js" ***!
************************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
var __webpack_error__ = new Error();
module.exports = new Promise((resolve, reject) => {
if(typeof home !== "undefined") return resolve();
__webpack_require__.l("http://localhost:3002/remoteEntry.js", (event) => {
if(typeof home !== "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__);
}, "home");
}).then(() => (home));
/***/ }
其实通过__webpack_require__.l 函数,加载远程模块入口文件http://localhost:3002/remoteEntry.js
javascript
var moduleMap = {
"./Content": () => {
return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_vue_vue"), __webpack_require__.e("src_components_Content_vue-_cddc0")]).then(() => (() => ((__webpack_require__(/*! ./src/components/Content */ "./src/components/Content.vue")))));
},
"./Button": () => {
return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_vue_vue"), __webpack_require__.e("src_components_Button_js")]).then(() => (() => ((__webpack_require__(/*! ./src/components/Button */ "./src/components/Button.js")))));
}
};
var init = (shareScope, initScope) => {
if (!__webpack_require__.S) return;
var name = "default"
var oldScope = __webpack_require__.S[name];
if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");
__webpack_require__.S[name] = shareScope;
return __webpack_require__.I(name, initScope);
};
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;
};
// This exports getters to disallow modifications
__webpack_require__.d(exports, {
get: () => (get),
init: () => (init)
});
加载完成后,执行加载模块remoteEntry.js里面暴露的init方法, 把当前上下文中的__webpack_require__.S和initScope作为参数
var initFn = (module) => (module && module.init && module.init(__webpack_require__.S[name], initScope))
接下来执行init函数时,需要明确一下是在remoteEntry.js这个文件上下文中执行的。里面也有一套同样的webpack模块加载函数和存储变量,虽然命名一样。
init方法里面,通过内部__webpack_require__.S进行存储共享依赖,name也是对应'default',直接复用前面注册在S上的vue依赖,再次调用__webpack_require.I__, 注册自己的依赖。
ini
__webpack_require__.S[name] = shareScope;
里面的这段很重要shareScope是在host应用中把__webpack_require__.S[name]作为参数传入的,本质上是src/main.js(host)文件和remoteEntry.js(remote)文件之间,通过全局变量,传递各自上下文中的内部变量(__webpack_require__.S)上的共享依赖。
所以等价于
css
remote___webpack_require__.S[name] = host___webpack_require__.S[name]
消费者和生产者里面注册的模块是同一个所以注册完成后__webpack_require__.S里面还是只有一个3.3.7版本的vue
到这里共享依赖初始化完成了。
3.2.4.4 webpack_require.f.j
继续执行f上挂载的方法remoes,此时src/main.js模块,没有在chunkMapping里面没有映射到对应的共享依赖,直接跳过了remotes方法,继续走后面的处理逻辑,这里可以直接跳过,最后通过__webpack_require__.f.j 函数加载src/main.js文件。同前面异步加载逻辑一样,放到__webpack_module__变量里面,
markdown
{
/***/ "./src/main.js":
/*!*********************!*\
!*** ./src/main.js ***!
*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ "webpack/sharing/consume/default/vue/vue");
/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(vue__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _Layout_vue__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Layout.vue */ "./src/Layout.vue");
const Content = (0,vue__WEBPACK_IMPORTED_MODULE_0__.defineAsyncComponent)(() => __webpack_require__.e(/*! import() */ "webpack_container_remote_home_Content").then(__webpack_require__.t.bind(__webpack_require__, /*! home/Content */ "webpack/container/remote/home/Content", 23)));
const Button = (0,vue__WEBPACK_IMPORTED_MODULE_0__.defineAsyncComponent)(() => __webpack_require__.e(/*! import() */ "webpack_container_remote_home_Button").then(__webpack_require__.t.bind(__webpack_require__, /*! home/Button */ "webpack/container/remote/home/Button", 23)));
const app = (0,vue__WEBPACK_IMPORTED_MODULE_0__.createApp)(_Layout_vue__WEBPACK_IMPORTED_MODULE_1__["default"]);
app.component('content-element', Content);
app.component('button-element', Button);
app.mount('#app');
/***/ })
}
main.js里面引用Content和Button组件,构建时转换成 __webpack_require__.e(/*! import() */ "webpack_container_remote_home_Content")函数,跟[前面的src_main_js加载类似],开始遍历执行__webpack_require__.f上的挂载方法,执行到__webpack_require__.f.consumes时,没有映射到共享依赖跳过,继续执行到__webpack_require__.f.remotes
3.2.4.5 webpack_require.f.remotes
ini
var chunkMapping = {
"webpack_container_remote_home_Content": [
"webpack/container/remote/home/Content"
],
"webpack_container_remote_home_Button": [
"webpack/container/remote/home/Button"
]
};
var idToExternalAndNameMapping = {
"webpack/container/remote/home/Content": [
"default",
"./Content",
"webpack/container/reference/home"
],
"webpack/container/remote/home/Button": [
"default",
"./Button",
"webpack/container/reference/home"
]
};
__webpack_require__.f.remotes = (chunkId, promises) => {
if(__webpack_require__.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
var getScope = __webpack_require__.R;
if(!getScope) getScope = [];
var data = idToExternalAndNameMapping[id];
if(getScope.indexOf(data) >= 0) return;
getScope.push(data);
if(data.p) return promises.push(data.p);
var onError = (error) => {
if(!error) error = new Error("Container missing");
if(typeof error.message === "string")
error.message += '\nwhile loading "' + data[1] + '" from ' + data[2];
__webpack_require__.m[id] = () => {
throw error;
}
data.p = 0;
};
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);
}
}
var onExternal = (external, _, first) => (external ? handleFunction(__webpack_require__.I, data[0], 0, external, onInitialized, first) : onError());
var onInitialized = (_, external, first) => (handleFunction(external.get, data[1], getScope, 0, onFactory, first));
var onFactory = (factory) => {
data.p = 1;
__webpack_require__.m[id] = (module) => {
module.exports = factory();
}
};
handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);
});
}
}
可以发现webpack_container_remote_home_Content在chunkMapping里面已经声明了,chunkMapping保存依赖的远程应用具体模块id,idToExternalAndNameMapping则保存远程模块的具体信息,看最后的执行入口函数
handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);
scss
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);
}
}
data[2]表示依赖的远程应用资源 "webpack/container/reference/home",直接执行
php
var promise = fn(arg1, arg2);
=>
__webpack_require__("webpack/container/reference/home", 0)
这个require在前面已经执行完成了,主要请求remoteEntry.js文件,存在缓存,直接从__webpack_module_cache__里面获取执行结果,也就是export上抛出的get和init方法。
正在promise.then方法里面,执行传入的next函数,也就是第5个参数onExternal
ini
var onExternal = (external, _, first) => (external ? handleFunction(__webpack_require__.I, data[0], 0, external, onInitialized, first) : onError());
继续执行handleFunction函数
sql
handleFunction(__webpack_require__.I, 'default', 0, external, onInitialized, first)
php
var promise = fn(arg1, arg2);
=>
__webpack_require__.I('default', 0),
就是前面在consumes方法里面,寻找并注册共享依赖,执行完成后继续执行next方法,也就是第5个参数onInitialized
kotlin
var onInitialized = (_, external, first) => (handleFunction(external.get, data[1], getScope, 0, onFactory, first));
kotlin
handleFunction(external.get, './Content', getScope, 0, onFactory, first )
这里external就是第一次执行__webpack_require__("webpack/container/reference/home", 0),返回的promise结果,也就是远程应用remoteEntry.js里面的内容
回到remoteEntry.js里面,可以发现get方法
javascript
var moduleMap = {
"./Content": () => {
return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_vue_vue"), __webpack_require__.e("src_components_Content_vue-_cddc0")]).then(() => (() => ((__webpack_require__(/*! ./src/components/Content */ "./src/components/Content.vue")))));
},
"./Button": () => {
return Promise.all([__webpack_require__.e("webpack_sharing_consume_default_vue_vue"), __webpack_require__.e("src_components_Button_js")]).then(() => (() => ((__webpack_require__(/*! ./src/components/Button */ "./src/components/Button.js")))));
}
};
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;
};
moduleMap['./Content'](),这里同样有__webpack_require__.e,但在是在远程应用的执行上下文里面,跟当前主应用的执行环境不同,这里是远程应用根据自己的webpack配置生成的webpack_require相关函数,也是真正打包构建'./Content'共享组件的地方。
继续递归,这个get方法执行完成后,promise.then里面的next方法,也就是onFactory函数,
ini
var onFactory = (factory) => {
data.p = 1;
__webpack_require__.m[id] = (module) => {
module.exports = factory();
}
};
ini
__webpack_require__.m = __webpack_modules__;
把获取到的./Content函数主体,从远程应用的上下文,重新赋值到当前host应用的__webpack_modules__变量下,
后续再遇到引入./Content组件的场景,直接从__webpack_modules__上获取,
回到src_main_js的实现部分\],继续引入Button.js组件,执行后续操作 ### 3.2.5 整体组件加载执行过程  *** ** * ** *** **👉 下一篇** :[彻底搞懂 Module Federation(下):Runtime API 与项目实操](https://juejin.cn/post/7615446397371154482 "https://juejin.cn/post/7615446397371154482")