首页 / 资讯中心 / 文章详情

Module Federation 嵌套远程组件加载实战:基于 nested-remote 示例解析 Host → Remote → Remote 的链式依赖

Module Federation 嵌套远程组件加载实战:基于 nested-remote 示例解析 Host → Remote → Remote 的链式依赖 ★ FEATURED ARTICLE
示例工程【免费下载链接】module-federation-examplesImplementation examples of module federation , by the creators of module federation项目地址https://gitcode.com/gh_mirrors/mo/module-federation-examples点击查看免费下载本指南以 module-federation-examples 仓库中的 nested-remote 示例为核心讲解如何用 Module Federation 实现「远程组件内部再加载远程组件」的嵌套场景Hostapp1异步加载 app2 暴露的ButtonContainer而 app2 自身又异步加载 app3 暴露的Button。读完本文你将掌握三应用链式联邦的架构设计、webpack 配置要点、React.lazy Suspense 的异步加载写法以及如何用 Playwright 端到端验证嵌套链路是否真的打通。什么是嵌套远程Nested Remote常规的 Module Federation 演示通常只覆盖「一个 Host 加载一个 Remote」的两层结构。但真实业务中被加载的远程应用本身也可能是一个独立的应用它内部还需要继续依赖别的远程应用——这就形成了嵌套Nested远程链路。nested-remote 示例用三个应用精确演示了这一场景app1Host 应用异步加载来自app2的ButtonContainer组件app2独立可运行的应用对外暴露ButtonContainer组件而该组件内部又异步加载来自app3的Buttonapp3独立可运行的应用对外暴露最底层的Button组件。链路可以简化为app1 →(remote) app2 →(remote) app3。关键在于app2 既是被 app1 消费的 Remote同时也是消费 app3 的 Host或者说中间层 Remote。这正是嵌套联邦与普通联邦的本质区别——中间层应用必须同时具备exposes和remotes两种能力。一键启动三个应用在仓库根目录或nested-remote目录下执行pnpm run start根级 package.json 中start脚本定义为start: pnpm --filter nested_app* --parallel start它会通过 pnpm workspace 并行启动nested_app1、nested_app2、nested_app3三个子包分别对应 app1、app2、app3 的 package.json、package.json、package.json它们均被 pnpm-workspace.yaml 的nested-remote/*规则纳入 workspace。每个子包的start脚本实际执行webpack-cli serve即通过 webpack-dev-server 启动开发服务三个应用分别监听端口应用角色http://localhost:3001app1HOST宿主http://localhost:3002app2STANDALONE REMOTE独立远程兼中间层http://localhost:3003app3STANDALONE REMOTE独立远程叶子节点浏览器打开localhost:3001页面会依次显示 app1 的内容、app2 的容器以及来自 app3 的按钮证明嵌套链路在运行时真实打通。此外每个子包还提供buildwebpack --mode production与serveserve dist -p 300x脚本可用于生产构建后静态托管。架构拆解三个应用的 webpack 联邦配置三个应用的 webpack 配置均使用module-federation/enhanced/webpack提供的ModuleFederationPlugin但职责各不相同。app1纯 Host声明对 app2 的远程依赖app1/webpack.config.js 的核心联邦配置plugins: [ new ModuleFederationPlugin({ experiments: { asyncStartup: true }, name: app1, shareStrategy: loaded-first, remotes: { app2: app2${getRemoteEntryUrl(3002)}, }, shared: { react: { singleton: true }, react-dom: { singleton: true } }, }), new HtmlWebpackPlugin({ template: ./public/index.html, }), ],要点说明remotes.app2指向app2//localhost:3002/remoteEntry.js由getRemoteEntryUrl(3002)生成即 app2 对外发布的联邦入口文件shareStrategy: loaded-first表示共享依赖采用「先加载者优先」的解析策略结合shared中react、react-dom的singleton: true保证整个链路中 React 只存在一份实例避免 hooks 状态错乱experiments: { asyncStartup: true }启用异步启动能力让远程模块加载与本地入口初始化可以并行是嵌套场景下提升首屏体验的关键实验特性。app2既是 Remote 又是 Host 的中间层app2/webpack.config.js 同时配置了exposes与remotes这是嵌套联邦的核心plugins: [ new ModuleFederationPlugin({ experiments: { asyncStartup: true }, name: app2, shareStrategy: loaded-first, filename: remoteEntry.js, exposes: { ./ButtonContainer: ./src/ButtonContainer, }, remotes: { app3: app3${getRemoteEntryUrl(3003)}, }, shared: { react: { singleton: true }, react-dom: { singleton: true } }, }), new HtmlWebpackPlugin({ template: ./public/index.html, }), ],filename: remoteEntry.js指定远程入口文件名Hostapp1的remotes.app2正是解析这个地址exposes[./ButtonContainer]将本地./src/ButtonContainer组件暴露给 app1remotes.app3声明 app2 自身对 app3 的远程依赖——这意味着当 app1 加载app2/ButtonContainer时Module Federation 运行时还会按需再去拉取 app3 的remoteEntry.js。app3叶子 Remote只暴露组件app3/webpack.config.js 是链路的末端不需要声明任何remotesplugins: [ new ModuleFederationPlugin({ experiments: { asyncStartup: true }, name: app3, shareStrategy: loaded-first, filename: remoteEntry.js, exposes: { ./Button: ./src/Button, }, shared: { react: { singleton: true }, react-dom: { singleton: true } }, }), new HtmlWebpackPlugin({ template: ./public/index.html, }), ],三个配置还共享两个通用细节output.publicPath: auto让资源 URL 根据当前页面地址自动推导保证开发webpack-dev-server与部署静态托管环境都能正确加载devServer.headers统一开启了Access-Control-Allow-Origin: *等 CORS 头允许跨端口加载远程脚本。getRemoteEntryUrl本地与 CodeSandbox 环境的自适应三个配置文件都定义了同名的getRemoteEntryUrl(port)工具函数见 app1/webpack.config.jsfunction getRemoteEntryUrl(port) { const { CODESANDBOX_SSE, HOSTNAME } process.env; // Check if the example is running on codesandbox if (!CODESANDBOX_SSE) { return //localhost:${port}/remoteEntry.js; } const parts HOSTNAME.split(-); const codesandboxId parts[parts.length - 1]; return //${codesandboxId}-${port}.sse.codesandbox.io/remoteEntry.js; }它优先返回//localhost:port/remoteEntry.js当检测到CODESANDBOX_SSE环境变量运行在 CodeSandbox 中时则从HOSTNAME中解析出沙箱 ID拼出对应的sse.codesandbox.io地址。该函数体现了示例对「本地运行 在线沙箱」双环境的兼容设计。组件级异步加载React.lazy Suspense 的逐层透传嵌套远程的加载不是一次性完成的而是由每一层的组件各自负责异步拉取下一层。app1 的入口组件app1/src/App.jsimport React from react; const RemoteButtonContainer React.lazy(() import(app2/ButtonContainer)); const App () ( div h1Nested/h1 h2App 1/h2 papp 1 body/p React.Suspense fallbackLoading Button Container RemoteButtonContainer / /React.Suspense /div );import(app2/ButtonContainer)中的app2前缀对应 webpack 配置里remotes的 keywebpack 会将其编译为对 app2remoteEntry.js的运行时加载调用。React.lazy将远程模块包装成懒加载组件外层用React.Suspense提供「Loading Button Container」占位文案。app2 的 ButtonContainer中间层组件的二次远程加载app2/src/ButtonContainer.js 是嵌套的核心组件import React from react; const RemoteButton React.lazy(() import(app3/Button)); const style { padding: 12, backgroundColor: #cccccc, }; const ButtonContainer () ( div style{style} App 2 Container br / br / React.Suspense fallbackLoading Button RemoteButton / /React.Suspense /div );它同时扮演两个角色作为 app2 暴露给 app1 的「导出组件」以及作为消费 app3 的「宿主组件」。组件内部再次使用React.lazy(() import(app3/Button))触发对 app3 的加载并套上独立的Suspense边界。于是运行时的加载时序是app1 先拉取 app2 的 remoteEntry → 渲染 ButtonContainer → 触发 app3 的 remoteEntry 加载 → 渲染 Button。app3 的 Button链路末端的真实渲染app3/src/Button.js 最终渲染带 aquamarine 背景色的按钮import React from react; const style { padding: 12, backgroundColor: aquamarine, }; const Button () button style{style}App 3 Button/button; export default Button;每个应用都可独立运行注意 app2 与 app3 的入口文件 app2/src/App.js 与 app3/src/App.js 分别直接引用了本地组件import ButtonContainer from ./ButtonContainer、import LocalButton from ./Button并同样通过 index.js 中的ReactDOM.render挂载到各自的#root。这说明中间层与叶子节点既是可被远程消费的联邦组件提供者又是可独立访问localhost:3002、localhost:3003单独运行验证的完整应用——这正是示例强调的 STANDALONE REMOTE 定位。端到端验证Playwright 如何证明嵌套链路生效仅凭浏览器肉眼观察不足以自动化回归仓库为每个应用都编写了 Playwright 用例e2e 目录从「元素存在性」与「样式来源」两个维度证明嵌套加载真实生效。checkApp1.spec.ts 访问 3001 端口并断言页面上存在可见的按钮且文本为App 3 Button——只有 app1 → app2 → app3 整条链路全部加载成功这个来自 app3 的按钮才会出现在 app1 的页面里根容器包含App 2 Container与app 1 body文本样式断言中App 2 Container容器的背景色为rgb(204, 204, 204)对应源码中的#cccccc按钮背景为rgb(127, 255, 212)对应aquamarine这些内联样式分别来自 app2 与 app3 的组件源码可作为「组件确实来自对应远程应用」的旁证。checkApp2.spec.ts 与 checkApp3.spec.ts 则分别验证 3002、3003 两个独立应用能自行渲染按钮及对应样式确保远程应用脱离 Host 也能独立工作。playwright.config.ts 通过webServer配置在跑测试前自动执行pnpm run start并等待http://localhost:3001就绪测试命令为pnpm run test:e2e # 或 pnpm run e2e:ci分别对应根级 package.json 中的test:e2e与e2e:ci脚本后者以--reporterlist输出。关于该仓库 Playwright 测试基础设施的通用约定公共辅助类BaseMethods、选择器、常量定义等可参考 playwright-e2e/README.md。关键配置速查与实战建议配置项取值本示例作用nameapp1/app2/app3联邦应用全局唯一标识也是 remote 引用的命名空间filenameremoteEntry.js远程入口文件名被上游remotes引用exposes./ButtonContainer、./Button声明对外暴露的模块及本地源文件映射remotesapp2...、app3...声明本应用依赖的远程入口地址sharedreact、react-dom均singleton: true跨应用共享单例依赖避免多实例冲突shareStrategyloaded-first共享依赖解析策略已加载版本优先experiments.asyncStartuptrue异步启动允许远程加载与本地初始化并行output.publicPathauto自动推导资源公共路径适配多环境基于该示例的实践要点中间层必须同时具备 exposes 与 remotesapp2 是嵌套链路的枢纽只暴露不消费或反之都会使链路断裂共享依赖统一配置 singleton三个应用对react、react-dom采用一致的共享策略是从源码结构看该示例保证嵌套场景下 React 单例运行的关键设计为每个远程组件提供 Suspense 边界逐层React.lazySuspense让每一跳的加载状态都有明确的占位反馈避免整页白屏保持 Remote 可独立运行app2、app3 各自的入口均直接渲染本地组件让远程应用既能被消费、又能单独调试用端到端测试固化链路契约断言「出现在 Host 页面中的按钮文本与样式来自末端应用」即可自动守护嵌套链路不被破坏。赞分享示例工程【免费下载链接】module-federation-examplesImplementation examples of module federation , by the creators of module federation项目地址https://gitcode.com/gh_mirrors/mo/module-federation-examples点击查看免费下载相关推荐module-federation-examples basic-host-remote用 Modern.js 搭建最小 Host-Remote 模块联邦示例module federation examples basic host remote用 Modern.js 搭建最小 Host Remote 模块联邦示例示例工程Module Federation 实战用 Vite Angular 构建 host/remote 微前端module-federation-vite-angular 示例解析Module Federation 实战用 Vite Angular 构建 host/remote 微前端module federation vite示例工程基于 Rsbuild 的 Create React App 微前端示例Host 与 Remote 的 Module Federation 实践指南基于 Rsbuild 的 Create React App 微前端示例Host 与 Remote 的 Module Federation 实践指南 导读 本文示例工程上一篇UAssetGUI终极指南无需虚幻引擎深度编辑.uasset资产的完整解决方案下一篇Apex Legends智能压枪系统终极指南5分钟实现自动武器识别与精准射击创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站