使用微前端升级 React
原文由 Alex O'Callaghan 于 发布,订阅该博客
2021 年,我们基于 React 17 部署了微前端架构。我们按照建议将 react 和 react-dom 设为共享的单例模块。这样整个应用在单一的 React 树中渲染。随着许多主流依赖要求使用 React 18,且 React 19 即将发布,我们必须在微前端中升级 React。由于涉及多个团队,这一升级必须分步进行。
引入桥接组件
2026 年更新:我们随后开源了自己的微前端 React 桥接包,详情可参阅 《开源我们的微前端 React 桥接方案》。本文介绍的是我们最初使用 @module-federation/bridge-react 包的实现方案。
为了让微前端在独立的 React 树中渲染,我们添加了一个“桥接”组件包装层。这样就能在不影响外壳应用的情况下升级微前端中的 React。
@module-federation/bridge-react 包为这种方案提供了一些辅助工具。在微前端内部,我们新增了一个 export-app.tsx 文件,将入口 React 组件用桥接组件包裹起来:
// src/export-app.tsx
import { createBridgeComponent } from "@module-federation/bridge-react";
import App from "./App";
export default createBridgeComponent({
rootComponent: App,
});
export const BRIDGE = true;我们还需要更新 ModuleFederationPlugin,将这个新的入口文件暴露出去。为了让微前端能够使用比外壳应用更高的版本,我们必须不再将 react 和 react-dom 作为单例共享。
// webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: "app",
filename: "remoteEntry.js",
exposes: {
"./App": "./src/export-app",
},
shared: ["react", "react-dom"],
}),
],
};在外壳应用中,我们使用 import-remote 动态加载 remoteEntry 文件。为了向后兼容,我们需要调整这一逻辑,使其既能加载直接暴露的 React 组件,也能加载经过桥接的组件:
import { createRemoteComponent } from '@module-federation/bridge-react';
const SomeRemote = React.lazy(async () => {
const remote = await importRemote({
url: '/bundles/remote-app',
scope: 'remote-app',
module: 'App',
});
if (remote.BRIDGE) {
return {
default: createRemoteComponent({
loader: () => remote,
loading: <div>Loading...</div>,
fallback: (info) =>
<div>Failed to load {info.error.message}</div>,
});
}
return remote;
}
});这里有几点需要说明:
- 我们使用
importRemote懒加载remoteEntry.js文件,从而避免加载不需要的微前端。 - 我们检查导出的常量
BRIDGE,它用于判断该微前端是否已用桥接组件包裹。这让各团队可以自行选择是否使用桥接,同时也能保持对尚未迁移的微前端的兼容。 - 我们将桥接组件放在一个带有
default属性的对象中返回,以模拟React.lazy所期望的动态加载模块的结构。
通过这些改动,我们就可以将各个微前端单独升级到 React 18,而无需升级外壳应用。等到所有微前端都采用桥接方案后,我们便可以在不强制升级微前端的情况下单独升级外壳应用。
随机一篇博客
评论
登录后参与讨论