一个用于运行时动态加载远程 React 组件的工具库,支持多版本共存、CDN 故障转移和完整的模块生命周期管理。
这是一个 pnpm monorepo,包含:
| 包名 | 版本 | 说明 |
|---|---|---|
mf-runtime-libs |
v1.0.4 | 核心运行时加载工具库 |
@react-mf-lib/react-adapter |
v1.0.1 | React 适配层 |
@react-mf-lib/vue-adapter |
v1.0.1 | Vue 3 适配层(在 Vue 中加载 React 组件) |
| 应用 | 说明 |
|---|---|
demo-bridge-host |
Bridge 模块宿主应用 demo |
demo-bridge-provider |
Bridge 模块远程组件提供者 demo |
test-mf-unpkg |
远程组件示例应用(React) |
host-react18-remote |
React 18 宿主应用示例 |
host-vue3-remote |
Vue 3 宿主应用示例(消费 React 组件) |
- 🚀 运行时动态加载 - 无需重新构建即可加载远程组件
- 📦 多版本支持 - 支持同一包的多个版本同时运行
- 🔄 CDN 故障转移 - 自动在多个 CDN 之间切换,提高可用性
- 💾 智能缓存 - 内置版本缓存机制,减少网络请求
- 🎯 TypeScript 支持 - 完整的类型定义
- ⚛️ React 友好 - 专为 React 组件 Module Federation 设计
- 🔧 可扩展 - 插件系统支持自定义扩展
- 📊 性能优化 - 预加载、卸载、健康检查
- 🔗 事件总线 - 跨模块通信支持
- ✅ 质量保障 - 155+ 单元测试,高覆盖率
- 🌉 Bridge 模块 - 支持懒加载远程组件和预加载
npm install mf-runtime-libs
# 或
pnpm add mf-runtime-libs
# 或
yarn add mf-runtime-libsimport { loadRemoteMultiVersion } from 'mf-runtime-libs';
async function loadRemoteComponent() {
const { scopeName, mf } = await loadRemoteMultiVersion({
name: 'my-remote-app',
pkg: '@myorg/remote-app',
version: '1.0.0',
});
const mod = await mf.loadRemote(`${scopeName}/Button`);
return mod.default;
}import { createLazyComponent, loadRemoteMultiVersion } from 'mf-runtime-libs';
const RemoteButton = createLazyComponent({
loader: () => loadRemoteMultiVersion({
name: 'remote',
pkg: '@org/remote-pkg',
version: '1.0.0',
}).then(({ mf }) => mf.loadRemote('remote/Button')),
loading: <div>Loading...</div>,
fallback: ({ error }) => <div>Error: {error.message}</div>,
});
function App() {
return <RemoteButton variant="primary" />;
}import { prefetchComponent } from 'mf-runtime-libs';
// 预加载远程组件资源
prefetchComponent({
id: 'remote/Button',
preloadComponentResource: true,
});import { useLazyComponent, loadRemoteMultiVersion } from 'mf-runtime-libs';
function MyComponent() {
const { loading, error, Component } = useLazyComponent({
loader: () => loadRemoteMultiVersion({
name: 'remote',
pkg: '@org/remote-pkg',
version: '1.0.0',
}),
loading: <div>Loading...</div>,
fallback: ({ error }) => <div>Error: {error.message}</div>,
});
if (loading) return null;
if (error) return <div>Error: {error.message}</div>;
if (Component) return <Component />;
return null;
}import { lazyRemote, RemoteModuleProvider } from '@react-mf-lib/react-adapter';
import { Suspense } from 'react';
// 方式 1: lazyRemote
const RemoteDashboard = lazyRemote({
pkg: '@myorg/remote-app',
version: '^1.0.0',
moduleName: 'Dashboard',
scopeName: 'myorg',
});
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<RemoteDashboard userId={123} />
</Suspense>
);
}
// 方式 2: RemoteModuleProvider
function App() {
return (
<RemoteModuleProvider
pkg="@myorg/remote-app"
version="^1.0.0"
moduleName="Dashboard"
scopeName="myorg"
loadingFallback={<Spinner />}
errorFallback={(error, reset) => (
<div>
<p>Error: {error.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
/>
);
}import { VueRemoteModuleProvider } from '@react-mf-lib/vue-adapter';
export default {
template: `
<VueRemoteModuleProvider
pkg="@myorg/remote-app"
version="^1.0.0"
moduleName="Dashboard"
scopeName="myorg"
/>
`,
};创建懒加载远程组件的工厂函数。
import { createLazyComponent } from 'mf-runtime-libs';
const LazyComponent = createLazyComponent<T>(options);参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
loader |
() => Promise<T> |
✅ | 加载远程模块的函数 |
loading |
ReactNode |
✅ | 加载中的 UI |
fallback |
(errorInfo) => ReactNode |
✅ | 错误兜底 UI |
export |
string |
❌ | 导出名称,默认 'default' |
delayLoading |
number |
❌ | 延迟显示 loading 的毫秒数 |
dataFetchParams |
unknown |
❌ | 数据获取参数 |
noSSR |
boolean |
❌ | 是否禁用服务端渲染 |
用于懒加载远程组件的 React Hook。
import { useLazyComponent } from 'mf-runtime-libs';
const { loading, error, Component } = useLazyComponent({
loader: () => loadRemoteMultiVersion(options),
loading: <div>Loading...</div>,
fallback: ({ error }) => <div>Error: {error.message}</div>,
});返回值:
{
loading: boolean, // 是否正在加载
error: ErrorInfo | null, // 错误信息
Component: ComponentType<T> | null // 加载完成的组件
}预加载远程组件资源。
import { prefetchComponent } from 'mf-runtime-libs';
prefetchComponent({
id: 'remote/Component',
preloadComponentResource: true,
dataFetchParams: { userId: 123 },
});动态加载远程模块,支持多版本和故障转移。
import { loadRemoteMultiVersion } from 'mf-runtime-libs';
const { scopeName, mf } = await loadRemoteMultiVersion(options, plugins);参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
name |
string |
✅ | - | Module Federation 的名称 |
pkg |
string |
✅ | - | npm 包名 |
version |
string |
❌ | 'latest' |
版本号或 'latest' |
retries |
number |
❌ | 3 |
每个 CDN 的重试次数 |
delay |
number |
❌ | 1000 |
重试间隔(毫秒) |
localFallback |
string |
❌ | - | 本地兜底 URL |
cacheTTL |
number |
❌ | 86400000 |
缓存时间(毫秒) |
revalidate |
boolean |
❌ | true |
异步重新验证最新版本 |
shared |
Record<string, any> |
❌ | - | 自定义共享模块配置 |
返回值: Promise<{ scopeName: string, mf: ModuleFederationInstance }>
MF 实例方法:
const { scopeName, mf } = await loadRemoteMultiVersion(options);
// 加载暴露的模块
const module = await mf.loadRemote(`${scopeName}/Button`);
const Button = module.default;import { eventBus } from 'mf-runtime-libs';
// 订阅事件
const unsubscribe = eventBus.on('user-login', (user, meta) => {
console.log('User logged in:', user);
console.log('Event meta:', meta); // { timestamp, source, id }
});
// 发送事件
eventBus.emit('user-login', { id: 1, name: 'John' });
// 只触发一次的订阅
eventBus.once('notification', (msg) => {
console.log('Received once:', msg);
});
// 取消订阅
unsubscribe();
// 获取事件历史
const history = eventBus.getHistory('user-login');import {
checkVersionCompatibility,
satisfiesVersion,
parseVersion,
compareVersions,
getLatestVersion,
getStableVersions,
} from 'mf-runtime-libs';
// 检查版本兼容性
const result = checkVersionCompatibility('18.2.0', '^18.0.0', 'react');
console.log(result.compatible); // true
console.log(result.severity); // 'info' | 'warning' | 'error'
// 版本范围匹配
satisfiesVersion('1.5.0', '^1.0.0'); // true
satisfiesVersion('2.0.0', '~1.2.0'); // false
// 版本比较
compareVersions('2.0.0', '1.0.0'); // > 0
compareVersions('1.0.0', '1.0.0'); // 0# 同时启动 Provider 和 Host(推荐)
cd apps/demo-bridge-host
pnpm dev:all
# 或分别启动
# 1. 启动 Provider (端口 3001)
cd apps/demo-bridge-provider
pnpm dev
# 2. 启动 Host (端口 3002)
cd apps/demo-bridge-host
pnpm dev访问 http://localhost:3002 查看效果
# 1. 启动远程组件
pnpm --filter test-mf-unpkg dev
# 2. 启动 React 宿主应用
pnpm --filter host-react18-remote dev
# 3. 启动 Vue 宿主应用(消费 React 组件)
pnpm --filter host-vue3-remote devpnpm install# 构建所有包
pnpm build
# 构建单个包
pnpm --filter mf-runtime-libs build
pnpm --filter @react-mf-lib/react-adapter build
pnpm --filter @react-mf-lib/vue-adapter build
# 监听模式
pnpm --filter mf-runtime-libs dev# 运行所有测试
pnpm --filter mf-runtime-libs test
# 监听模式
pnpm --filter mf-runtime-libs test:watch
# 生成覆盖率报告
pnpm --filter mf-runtime-libs test --coverage# 格式化代码
pnpm format
# 代码检查
pnpm check
# 单个包
pnpm --filter mf-runtime-libs format
pnpm --filter mf-runtime-libs checkreact-mf-lib/
├── packages/
│ ├── mf-runtime-libs/ # 核心工具库 (v1.0.4)
│ │ ├── src/
│ │ │ ├── index.ts # 主入口
│ │ │ ├── loader/ # loadRemoteMultiVersion
│ │ │ ├── preload/ # 预加载模块
│ │ │ ├── unload/ # 卸载管理
│ │ │ ├── health/ # 健康检查
│ │ │ ├── version/ # 版本工具
│ │ │ ├── event-bus/ # 事件总线
│ │ │ ├── plugins/ # 插件系统
│ │ │ ├── hooks/ # React Hooks
│ │ │ ├── shared-state/ # 共享状态
│ │ │ └── bridge/ # Bridge 模块
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── create-lazy-component.tsx
│ │ │ ├── prefetch.ts
│ │ │ └── lazy-load-component-plugin.ts
│ │ ├── __tests__/ # 单元测试
│ │ ├── rslib.config.ts
│ │ └── package.json
│ ├── react-adapter/ # React 适配器 (v1.0.1)
│ │ └── src/
│ │ ├── components/ # RemoteModuleProvider, lazyRemote
│ │ └── hooks/ # useRemoteModuleHook
│ └── vue-adapter/ # Vue 适配器 (v1.0.1)
│ └── src/
│ ├── components/ # VueRemoteModuleProvider
│ ├── hooks/ # useVueRemoteModule
│ └── utils/ # mountReactToGlobal
└── apps/
├── demo-bridge-provider/ # Bridge Provider Demo (3001)
├── demo-bridge-host/ # Bridge Host Demo (3002)
├── test-mf-unpkg/ # 远程组件示例
├── host-react18-remote/ # React 18 宿主
└── host-vue3-remote/ # Vue 3 宿主
- 构建工具: Rslib, Rsbuild, Rspack
- 运行时: @module-federation/enhanced, @module-federation/bridge-react
- 包管理: pnpm (workspace)
- 代码规范: Biome
- 测试框架: Vitest + happy-dom
- 类型检查: TypeScript
- 版本管理: Changesets
// ✅ 推荐:生产环境使用固定版本
await loadRemoteMultiVersion({
name: 'my-app',
pkg: '@myorg/remote-app',
version: '1.2.3',
});
// ⚠️ 注意:使用 latest 时设置合理的 cacheTTL
await loadRemoteMultiVersion({
name: 'my-app',
pkg: '@myorg/remote-app',
version: 'latest',
cacheTTL: 3600000, // 1 小时
revalidate: true,
});// 在用户可能访问的路由预加载
useEffect(() => {
prefetchComponent({
id: 'remote/Dashboard',
preloadComponentResource: true,
});
}, []);
// 高优先级立即加载
prefetchComponent({
id: 'remote/CriticalModule',
priority: 'high',
});const MyLazyComponent = createLazyComponent({
loader: () => loadRemoteMultiVersion(options),
loading: <Spinner />,
fallback: ({ error, errorType }) => (
<ErrorFallback
error={error}
type={errorType} // 'LOAD_REMOTE' | 'DATA_FETCH' | 'RENDER'
onRetry={() => window.location.reload()}
/>
),
});import { unloadRemote } from 'mf-runtime-libs';
// 组件卸载时清理
useEffect(() => {
return () => {
unloadRemote({ name: 'my-app', pkg: '@myorg/remote-app' });
};
}, []);- 检查 CDN 地址是否可访问
- 查看浏览器控制台的错误信息
- 验证远程组件是否正确构建
- 检查 Module Federation 配置是否匹配
- 确认
remoteEntry.js可访问
确保库的构建配置正确外部化 React:
// rslib.config.ts
export default defineConfig({
tools: {
rspack: {
externals: ['react', 'react-dom', 'react/jsx-runtime'],
},
},
});- 确认共享模块的
singleton配置 - 检查 React 版本是否兼容
- 使用不同的
name避免命名冲突
- 确认远程组件已发布类型定义
- 检查 TypeScript 配置
- 使用
import type导入类型
- mf-runtime-libs 详细文档 - 核心工具库完整 API
- vue-adapter 文档 - Vue 3 适配层使用指南
- react-adapter 文档 - React 适配层使用指南
- 新增 Bridge 模块:
createLazyComponent、useLazyComponent - 新增
prefetchComponent预加载功能 - 新增
createLazyLoadComponentPlugin导出 - 完善错误处理和类型定义
- 重构:使用 mf-runtime-libs 替换 RemoteModuleCard
- 新增:完整的单元测试覆盖(155+ 测试)
- 新增:健康检查模块
- 新增:事件总线模块
- 新增:版本兼容性检查
ISC
欢迎提交 Issue 和 Pull Request!
- Fork 项目
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request