首页 / 文章 / 使用 Webpack Module Federation 实现 React 微前端

使用 Webpack Module Federation 实现 React 微前端

用 Webpack 5 Module Federation 替代 iframe 的解决方案,让主机端和远程的 React 应用共享运行时,能够独立部署,同时仍保持统一的产品体验。

1148 词

大型产品的界面往往不再是一个整体前端。结算页面、控制面板、设置选项以及这些组件周围的装饰元素通常属于不同的团队,每个团队都有各自的待办任务、技术债务和发布计划。这种架构通常被称为微前端:即各自独立开发的UI组件,但整体上仍表现为一个产品,相当于微服务在客户端的表现形式。

很长一段时间里,用于实现这种架构的React工具并不完善。企业要么维持一个庞大的单页应用包,要么将多个独立应用嵌入到iframe中并承担由此带来的种种问题。Webpack 5的模块联邦功能则提供了第三种解决方案:允许独立开发并部署JavaScript应用,这些应用在运行时会在同一个浏览器文档中组合在一起。对于任何设计多团队React系统的人来说,了解这一功能的原理以及它为何能取代旧有的解决方案都是非常重要的。

模块联邦出现之前的问题

在 Federation 发布之前,那些希望实现独立前端交付的团队选择有限。

默认方案是单体 SPA:一个代码库、一条构建流水线、一次部署。这种模式适合小型团队,但随着职责分散,问题也会随之增加。每个功能都共享同一份构建结果,代码库越大,编译时间就越长。某个模块出现问题就可能导致其他团队的发布计划停滞。协调多个团队之间的部署工作本身就成了一项复杂的项目管理任务。

另一种常见的解决方案是使用内嵌框架。将完整的子应用嵌入父页面中可以实现真正的独立部署,这也是许多企业选择它的原因。但这种方案很快就会带来诸多弊端:

  • 隔离性极强。样式、DOM和JavaScript上下文彼此独立,无法混用。在父组件与子组件之间共享状态、协调数据或统一设计语言,会变成复杂的工程任务,而非简单的属性传递。
  • 依赖项重复。每个帧通常都会加载自己的React版本、共享库和CSS文件,导致用户反复下载相同的字节数据。
  • 用户体验受损。跨帧的滚动、焦点切换、尺寸调整、深度链接以及历史记录功能都需要自定义实现,且使用体验仍不理想。
  • SEO和无障碍性受影响。框架内的内容更难被搜索引擎索引,对辅助技术的兼容性也较差。
  • 通信仅限于消息传递。跨帧数据传输只能通过postMessage和手动设计的协议实现,没有共享内存,也没有共享的React上下文。

Iframes解决了“独立部署”的问题,却引发了更严重的“干净集成”难题。真正缺失的是在不放弃单文档用户体验的前提下实现构建与部署时的独立性。

什么是模块联邦?

模块联邦是Webpack 5引入的一项技术,它允许独立构建和部署的JavaScript应用程序在**运行时**而非编译时共享代码。

在实际应用中,你可以运行多个来自不同团队、开发流程和发布线的应用程序,它们在浏览器中仍能整合为一个整体。一个应用程序提供组件、路由或辅助功能,另一个应用程序则可以像使用同一包中的内容一样调用它们,而无需在提供方发生变化时重新构建调用方。

这种运行时的组合方式正是当今React微前端技术的常见技术基础。

工作原理:主机、远程节点与共享依赖

Federation定义了两种角色:

  • 主机负责加载其他地方发布的代码,通常是用于嵌入其他团队UI的壳层。
  • 远程节点则为他人发布模块,包括页面、组件、钩子函数或工具类。

同一个应用可以同时扮演这两种角色:既提供某些模块,又使用其他模块。

远程节点在Webpack配置中声明要导出的内容;主机则指定要加载哪些远程节点以及需要导入哪些符号。加载操作是在浏览器中针对远程节点的入口URL进行的。主机的构建过程不需要远程节点的源代码,只需一个在应用运行时能够获取的稳定入口点即可。

共享依赖项进一步完善了这一方案。当主机端和远程端都需要 React 时,可以通过 Federation 指令让它们共享同一个 React 实例,而非分别部署两个。这样既避免了类似 iframe 的重复加载问题,又允许团队在必要时使用不同版本的 React。

Module Federation 与 Iframes 的对比

正是这种优势使得许多团队在 Federation 发展成熟后放弃了 Iframes。它保留了让 Iframes 受欢迎的独立部署特性,同时又不牺牲集成质量。远程组件可以直接操作主机端的 DOM,共享同一个 JavaScript 环境,并能重复使用各种提供者、状态管理工具以及设计系统相关包——而这些在 Iframes 的限制下要么极其困难,要么根本无法实现。

大型团队为何选择它

小型产品很少需要如此复杂的机制。那些拥有众多前端团队的大型组织会利用 Federation 来解决架构上的瓶颈问题:

  • 独立部署。各团队可以自行推送修复,无需重新构建其他团队的代码产物。
  • 团队自主性。每个小组都能按照自己的节奏、CI流程以及在一定范围内的工具选择来开展工作。
  • 更快的构建速度。由于是独立的部署环境,小的改动无需重新构建整个系统。
  • 逐步现代化。传统架构可以逐个新增部署环境,而无需一次性全面升级。
  • 技术栈的灵活性。共享框架最为简单,但团队有时也会通过联邦机制跨越不同主版本,甚至在更大努力下实现不同框架之间的整合。

实际应用场景

电商网站通常会让产品目录、结账和账户管理团队使用独立的远程环境。SaaS产品则由功能开发团队提供控制面板组件或设置界面,无需为每次更改都打开核心系统外壳。在旧界面仍在运行的同时,迁移过程可将传统单页应用的不同部分拆分到各个远程环境中。

主要优势总结

Federation的优势其实很简单:真正的独立部署能力、共享的运行时环境可避免库重复及脆弱的跨应用通信问题,同时还能保持统一的用户体验。它实现了所承诺的独立框架功能,却没有带来集成和性能方面的额外负担。

结论

从iframe转向Module Federation体现了前端架构的进一步成熟:部署独立性与一致的用户体验不再必然是对立的。Webpack 5使得那些已经无法使用单一代码包的、以React为主的应用组织能够实现这种结合。随着这一技术的普及,以及Rspack和Module Federation 2.0等工具对相同运行时共享理念的扩展,了解这一转变背后的原因有助于设计大型React系统的开发者有意识地确定代码归属边界,而非默认使用iframe或不断扩大的单体架构。

亲自尝试

可在react_module_federation找到一个最简化的主机/远程示例。

在本地克隆它:

git clone https://github.com/ModithaM/react_module_federation.git
cd react_module_federation

首先启动远程端,使其在主机请求模块之前就能提供入口点:

cd remote
npm install
npm start

在另一个终端中,启动主机:

cd host
npm install
npm start

在浏览器中打开该主机;它应在运行时加载远程组件,并展示上述关系。顺序很重要:单独启动的主机在远程端就绪之前没有任何内容可获取,这有助于提醒我们,Federation的独立性仍然取决于在shell启动时能够访问远程端。