模块联邦可作为插件系统底座,需将remotes视为动态注册/卸载的插件实例,统一导出含id、routes、init、destroy的插件对象,并由host统一调度生命周期与共享依赖。
模块联邦本身不是插件系统,但能成为插件系统的运行时底座——关键在于把 remotes 当作可动态注册/卸载的插件实例,而非静态配置项。
很多团队直接在 exposes 里写 ./Button 或 ./Dashboard,这会导致 host 无法感知插件能力边界,也无法做生命周期管理。真正可插拔的 remote 应该导出一个符合约定的插件对象:
register() 函数必须返回包含 id、routes、init、destroy 的对象,例如:{ id: 'user-management', routes: [...], init: () => {}, destroy: () => {} }
@company/plugin-core 的 Plugin 类型,避免字段拼写不一致或缺失ReactDOM.render 或挂载全局事件,host 才是唯一调度者;remote 的 init 只负责准备资源(如加载 locale、初始化 store slice)把 import('microfrontend1/register') 写死在 host 代码里,等于把插件列表编译进包,失去“运行时扩展”意义。正确做法是让 host 从远端配置中心或本地 JSON 文件读取插件元信息:
{ "id": "analytics", "url": "https://cdn.example.com/analytics/remoteEntry.js", "scope": "analytics" }
loadRemotePlugin({ url, scope }).then(m => m.register())
import() 动态导入不支持变量拼接 URL,所以必须用 Container API 手动初始化远程容器:__webpack_init_sharing__ + getContainer + init
多个 remote 同时使用不同版本的 react 或 react-router,会导致 Hooks 失效、Context 断裂、甚至白屏。这不是警告,是必现问题:
shared 配置中不能只写 ['react', 'react-dom'],必须显式指定 singleton: true 和 requiredVersion,例如:{ react: { singleton: true, requiredVersion: '^18.2.0' } }
shared 的唯一提供方(即 shareScope 主控者),所有 remote 设置 import: false,强制从 host 拿实例模块联邦不处理 unmount,你 import 过的 remote 模块会一直留在内存里,state、定时器、事件监听器全都不会自动清除:
destroy() 方法必须显式调用:clearInterval、removeEventListener、store.unsubscribe、unmountComponentAtNode
useRoutes + 状态驱动的路由数组,每次插件变更后重新生成 createBrowserRouter(routes)
insert-css 注入的 style 标签,需在 destroy() 里手动移除对应 <style> 节点最常被跳过的环节是插件间的类型对齐与错误边界——remote 导出的 register 返回值类型一旦和 host 期望的 Plugin 接口不一致,TS 编译通过但运行时报 Cannot read property 'routes' of undefined,这种错误不会出现在构建阶段,只会在特定插件启用时爆发。