示例工程【免费下载链接】module-federation-examplesImplementation examples of module federation , by the creators of module federation项目地址https://gitcode.com/gh_mirrors/mo/module-federation-examples点击查看免费下载comprehensive-demo-react18 是 module-federation-examples 仓库中的一个多框架综合示例其中位于 app-01/src/docs/Tabs.md 的文档完整演示了「宿主应用App #1通过 Module Federation 远程加载 App #2 的 Tabs 组件并让浏览器路由与 Tab 状态实时同步」的实战方案。本文将以该文档为主线结合仓库内真实源码与 E2E 测试逐层拆解远程 Tab 组件如何实现、如何与路由联动、如何按需懒加载第三个应用的样式化组件帮助你掌握一套可复用的「远程组件 路由同步 懒加载」联邦化开发模式。一、文档背后的演示场景App #1 中的 Routing DemoTabs.md 位于宿主应用 App #1 的文档目录中它描述了以下四个核心事实页面中的 Tab 组件是从bravo-app即 App #2运行在 3002 端口远程导入的浏览器的路由会随激活的 Tab 在/routing/foo与/routing/bar之间切换直接打开 http://localhost:3002App #2 的根页面可以看到完全相同的 Tab 组件Bar 这个 Tab 内还按需懒加载了来自 UI Library 演示App #33003 端口的 styled-componentsButton且仅在 Bar 被渲染时才加载。这正是 Module Federation 中「同一远程组件既可作为宿主页面内嵌模块、又可作为远程应用自身页面模块」的双重用法也展示了跨应用按需加载的完整链路App #1 → App #2Tabs→ App #3Button。二、文档如何在页面中呈现routing-page.jsx 与 Markdown 渲染链路Tabs.md 之所以能成为路由演示页的说明文档是因为 App #1 在构建时通过raw-loader将.md文件作为字符串导入见 app-01/webpack.config.js再由 app-01/src/Markdown.jsx 使用markdown-to-jsx渲染成 Material UI 风格的排版。路由演示页 routing-page.jsx 把「文档」与「远程组件」放在同一页面import Markdown from markdown-to-jsx; import Page from ../Page; import React from react; import Tabs from ../docs/Tabs.md; const RoutedTabs React.lazy(() import(app_02/Tabs)); const RoutingPage () ( Page titleRouting Demo Markdown{Tabs}/Markdown React.Suspense fallbackLoading Tabs... RoutedTabs / /React.Suspense /Page );这里import(app_02/Tabs)是app_02这个远程容器remote container的异步导入调用配合React.lazy与React.Suspense实现了远程组件的懒加载只有当用户进入/routing路由时Tabs 组件才会被拉取并渲染。该页面被挂载在 app-01/src/Routes.jsx 的/routing路径下。三、远程 Tab 组件的实现路由驱动切换的 Tabs.jsx文档所说的「路由随 Tab 变化」并非空话而是由 App #2 中的 Tabs.jsx 通过react-router-dom的 Hook 实现的。它用useRouteMatch()取得当前页面的基础路径在 App #1 内即/routing用useHistory()在切换 Tab 时改写 URL用useLocation()读取当前路径作为 Tab 的激活值const match useRouteMatch(); const history useHistory(); const location useLocation(); const { path: rootPath } match; const handleChange (event, newValue) { history.push(newValue); }; Tabs value{location.pathname} onChange{handleChange} Tab labelFoo value{${rootPath}/foo} / Tab labelBar value{${rootPath}/bar} / /Tabs随后通过Switch/Route将路由与内容绑定/routing精确匹配时重定向到/routing/foo/routing/foo渲染 Foo Content/routing/bar渲染 Bar Content。关键点在于这套组件并不关心自己运行在哪个应用里。它从match.path推导基础路径因此在 App #1 的/routing下表现为/routing/foo、/routing/bar而当它作为 App #2 根页面的一部分渲染时见 app-02/src/App.jsx基础路径退化为空路由就变成/foo、/bar。文档中「打开 3002 端口能看到同样的 Tab 组件」正是由此而来。四、Bar Tab 的按需懒加载跨应用加载 styled-components Button文档还强调了一个容易被忽略的细节Bar 内容里的Button来自 UI Library 演示App #3并且「仅在 Bar 被渲染时懒加载」。这在 Tabs.jsx 的顶部和 Bar 路由内均有体现const Button React.lazy(() import(app_03/Button));Bar 路由的内容中嵌套了React.SuspenseRoute path{${rootPath}/bar} Typography componentdiv Box p{3} Bar Content React.Suspense fallback{null} ButtonBar Button/Button /React.Suspense /Box /Typography /Route只有路由落在/routing/bar或 App #2 根部的/bar时app_03/Button才会被真正加载。该按钮是 app-03/src/Button.jsx 中一个典型的 styled-components 样式化组件const Button styled.button display: inline-block; border-radius: 3px; padding: 0.5rem 0; margin: 0.5rem 1rem; width: 11rem; background: palevioletred; color: white; font-size: 1rem; font-family: sans-serif; ;于是形成了「宿主 → 远程 Tab → 更远程的 Button」两级懒加载链条验证了 Module Federation 支持嵌套联邦remote 再消费 remote的能力。五、支撑这一切的联邦配置exposes、remotes 与 shared5.1 App #1宿主兼聚合方app-01/webpack.config.js 中的ModuleFederationPlugin声明了四个远程依赖其中就包含 App #2 与 App #3remotes: { app_02: app_02http://localhost:3002/remoteEntry.js, app_03: app_03http://localhost:3003/remoteEntry.js, app_04: app_04http://localhost:3004/remoteEntry.js, app_05: app_05http://localhost:3005/remoteEntry.js, },同时它以exposes对外提供./SideNav与./Page供 App #2 反向消费见下文 App #2 的 App.jsx。5.2 App #2既是被消费方又是消费方app-02/webpack.config.js 中name: app_02与filename: remoteEntry.js定义了远程容器的身份与入口文件exposes声明了对外暴露的模块exposes: { ./Dialog: ./src/Dialog, ./Tabs: ./src/Tabs, },remotes则声明了它自身的依赖——App #1 的Page组件与 App #3 的Buttonremotes: { app_01: app_01http://localhost:3001/remoteEntry.js, app_03: app_03http://localhost:3003/remoteEntry.js, },App #2 的根应用 App.jsx 正是通过import(app_01/Page)复用 App #1 的Page外壳顶栏 内容区再把本地Dialog与Tabs渲染进去从而与 App #1 的页面观感完全一致。5.3 共享依赖singleton 与 eager为避免 React 双实例问题两端配置都对核心依赖做了单例化处理。以 App #2 为例shared: { ...deps, material-ui/core: { singleton: true }, react-router-dom: { singleton: true }, react-dom: { singleton: true, requiredVersion: false, eager: true }, react: { singleton: true, requiredVersion: false, eager: true }, },singleton: true保证联邦各应用共用同一份react、react-dom、material-ui/core与react-router-dom避免 hooks 状态错乱requiredVersion: false放宽版本校验允许跨应用版本不一致时仍可共享eager: true让核心依赖在容器启动时同步就绪避免加载时序问题此外experiments: { asyncStartup: true }开启了异步启动实验特性shareStrategy: loaded-first指定共享依赖采用「先加载者优先」策略。值得说明的是App #2 同时提供了 webpack 与 rspack 两套构建配置webpack.config.js 与 rspack.config.js二者在联邦配置上保持了一致仅构建器与 loader 不同便于对比两种工具链下的联邦实现。此外两端 devServer 都开启了 CORS 头Access-Control-Allow-Origin: *这是浏览器端跨端口拉取remoteEntry.js与联邦模块的必要前提。六、文档中「路由即 URL」的可验证性Playwright E2E 测试文档所描述的行为已被仓库内的 E2E 测试固化。在 comprehensive-demo-react18/e2e/checkApp1.spec.ts 中routing page renders tabs用例完整验证了整条链路test(routing page renders tabs, async ({ page }) { await page.goto(${base}/#/routing/foo); ... await expect(page.getByRole(tab, { name: Foo })).toBeVisible(); await expect(page.getByText(Foo Content)).toBeVisible(); await page.getByRole(tab, { name: Bar }).click(); await expect(page.getByText(Bar Content)).toBeVisible(); const barButton page.getByRole(button, { name: Bar Button }); await expect(barButton).toBeVisible(); await expect(barButton).toHaveCSS(background-color, rgb(219, 112, 147)); });该用例同时断言了 Tabs.md 中的全部四段文字routingParagraphs见 checkApp1.spec.ts并验证了路由进入#/routing/foo时 Foo Tab 激活、Foo 内容可见点击 Bar Tab 后 Bar 内容可见且 Bar Button 懒加载成功rgb(219, 112, 147)正是 App #3 中palevioletred的解析结果证明远程样式化按钮确实渲染生效。同一测试文件还以{ name: Routing, hash: #/routing/foo }断言了侧边栏中 Routing 入口的链接checkApp1.spec.ts并将#/routing/foo作为文档锚点链接的一部分checkApp1.spec.ts与文档「浏览器路由是 /routing/foo|bar」的描述相互印证。七、如何运行与体验在 comprehensive-demo-react18 目录下按序执行yarn install pnpm run start随后打开 http://localhost:3001从左侧导航进入Routing页面即可复现本文所述场景再打开 http://localhost:3002 对比同一 Tabs 组件在远程应用根部的表现。整个 demo 由五个应用构成App #13001宿主外壳→ App #23002Material UI 组件提供方→ App #33003styled-components UI 库→ App #43004Svelte→ App #53005LitElement。运行 E2E 测试可执行pnpm test:e2e或使用pnpm test:e2e:ui打开交互式调试界面见 comprehensive-demo-react18/package.json。八、小结从本文可复用的三件事通过 Tabs.md 与配套源码可以提炼出三种可直接迁移到自有项目的实践远程组件的路由自适应远程组件用useRouteMatch().path而非硬编码路径推导基础路由就能在同一份代码下同时服务于宿主子路由/routing/foo与远程应用根路由/foo两级懒加载React.lazySuspense可以让「宿主 → 远程 → 再远程」的嵌套依赖按需加载配合exposes/remotes的容器声明即可实现组件级代码拆分以文档驱动演示将 Markdown 说明文件作为页面的一部分raw-loader导入 markdown-to-jsx渲染再配合同页面的可交互远程组件与 E2E 断言让「文档描述、代码实现、自动化验证」三者保持一致。如需继续深入可对照阅读同目录下的 Dialog.mdReact Portal 场景的远程 Dialog与 UiLibrary.md独立 CSS 方案下的远程 Button它们共同构成了 comprehensive-demo-react18 中「跨应用组件复用」的完整示例集。赞分享示例工程【免费下载链接】module-federation-examplesImplementation examples of module federation , by the creators of module federation项目地址https://gitcode.com/gh_mirrors/mo/module-federation-examples点击查看免费下载相关推荐跨应用远程 Tab 组件实战基于 module-federation-examples 的嵌套路由与惰性加载解析跨应用远程 Tab 组件实战基于 module federation examples 的嵌套路由与惰性加载解析 导读 本文以 module federati示例工程Module Federation 实战在 comprehensive-demo-react16 中懒加载远程 Dialog 组件并用 React Portal 渲染Module Federation 实战在 comprehensive demo react16 中懒加载远程 Dialog 组件并用 React Porta示例工程module-federation-examples 之 comprehensive-demo-react18React 18 多框架模块联邦综合演示实战指南module federation examples 之 comprehensive demo react18React 18 多框架模块联邦综合演示实战指南示例工程上一篇SubFinder跨平台兼容性终极指南Windows/macOS/Linux环境全面对比下一篇Evergreen UI自定义组件开发扩展现有组件的5种实用模式创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考