彻底搞懂 Module Federation(中下):MF 模块加载(下)

本文是《彻底搞懂 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 整体组件加载执行过程 ![image-6.png](https://oss.xyyzone.com/jishuzhan/article/2031548403260391426/c139ac839e0c73862d10e1871a130b05.webp) *** ** * ** *** **👉 下一篇** :[彻底搞懂 Module Federation(下):Runtime API 与项目实操](https://juejin.cn/post/7615446397371154482 "https://juejin.cn/post/7615446397371154482")

相关推荐
拖拉斯旋风1 小时前
从零到一:用 Node.js + LangChain + Milvus 打造《天龙八部》专属 RAG 问答机器人
前端
独特的账号1 小时前
前端浏览器插件的开发一步搞定
前端·react.js
李剑一1 小时前
超实用!数字孪生 Cesium 园区 3D 模型加载,一次学会的保姆级教程
前端·vue.js·cesium
游魂Andy1 小时前
零成本搭建专属AI助手:OpenClaw永久免费部署全攻略
前端·人工智能·ai编程
wuhen_n2 小时前
动态组件与 keep-alive:如何优化页面切换体验与性能?
前端·javascript·vue.js
wuhen_n2 小时前
插槽的作用域与分发:如何让组件更灵活、可定制?
前端·javascript·vue.js
IT_陈寒2 小时前
Vite凭什么比Webpack快10倍?5个核心优化原理大揭秘
前端·人工智能·后端
gyx_这个杀手不太冷静2 小时前
OpenCode 进阶使用指南(第三章:MCP 集成)
前端·ai编程
摸鱼的春哥2 小时前
你适合养龙虾🦞吗?4类人不适合2类适合
前端·javascript·后端