如何通过模块联邦从Shell项目程序化引导MFE的路由、NgRx等?
程序化引导MFE的路由、NgRx及依赖配置方案
针对你在Shell中懒加载MFE模块时,需要确保所有必要配置(路由、NgRx实例等)完整加载初始化的需求,以下是几种实用实现方式:
1. 扩展模块加载后的初始化钩子
让MFE模块暴露专门的初始化函数,Shell在加载模块后手动触发,将Shell侧的核心依赖(如NgRx Store、Router)传入,由MFE自主完成配置注册。
MFE侧实现
在MFE的模块文件中导出初始化逻辑:
// MFE/src/app/my-sub/my-sub.module.ts import { Store } from '@ngrx/store'; import { Router } from '@angular/router'; import { registerTodoFeature } from './store/todo.feature'; import { todoRoutes } from './todo.routes'; // 暴露给Shell的初始化函数 export function initMfeDependencies(store: Store, router: Router) { // 注册NgRx Feature Store registerTodoFeature(store); // 动态添加MFE路由到Shell路由表 router.config.unshift(...todoRoutes); // 其他第三方库初始化(如SDK、全局配置) } export class MySubModule { // 可选:也可以在模块构造函数中自动执行初始化 constructor(private store: Store, private router: Router) { initMfeDependencies(store, router); } }
Shell侧修改加载逻辑
// Shell/src/app/routes.ts import { inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { Router } from '@angular/router'; import { loadRemoteModule } from '@angular-architects/module-federation'; const routes = [ { path: 'todo', loadChildren: () => loadRemoteModule({ type: 'module', remoteEntry: 'http://localhost:4300/remoteEntry.js', exposedModule: './MySubModule', }).then((m) => { // 手动触发MFE初始化(如果MFE没有在构造函数自动执行) const shellStore = inject(Store); const shellRouter = inject(Router); m.initMfeDependencies?.(shellStore, shellRouter); return m.MySubModule; }), }, ];
2. 用APP_INITIALIZER提前加载MFE配置
如果MFE的配置需要在模块加载前就就绪,可以在Shell的根模块中使用APP_INITIALIZER提前拉取MFE的配置文件,存入全局上下文后再加载MFE模块。
Shell侧预加载配置
// Shell/src/app/app.module.ts import { APP_INITIALIZER, NgModule } from '@angular/core'; import { HttpClient } from '@angular/common/http'; // 预加载MFE配置 function loadMfeConfig(http: HttpClient) { return () => http.get('http://localhost:4300/mfe-config.json') .toPromise() .then(config => { // 将配置存入全局对象,供MFE读取 if (!window['mfeConfigs']) window['mfeConfigs'] = {}; window['mfeConfigs']['mySubModule'] = config; }) .catch(err => console.error('MFE配置加载失败:', err)); } @NgModule({ providers: [ { provide: APP_INITIALIZER, useFactory: loadMfeConfig, deps: [HttpClient], multi: true } ] }) export class AppModule {}
MFE侧读取配置初始化
// MFE/src/app/my-sub/my-sub.module.ts export class MySubModule { constructor(private store: Store, private router: Router) { const mfeConfig = window['mfeConfigs']?.['mySubModule']; if (mfeConfig) { // 根据配置注册NgRx Feature registerTodoFeature(store, mfeConfig.storeConfig); // 根据配置添加路由 router.config.unshift(...mfeConfig.routes); } } }
3. 拆分初始化模块与业务模块
通过Module Federation将MFE的初始化逻辑单独暴露为一个模块,Shell先加载并执行初始化,再加载业务模块,确保配置先于业务代码就绪。
MFE侧配置暴露模块
// MFE/module-federation.config.js module.exports = { name: 'mySubMfe', exposes: { './MySubModule': './src/app/my-sub/my-sub.module.ts', './MfeInit': './src/app/mfe-init.ts' // 单独暴露初始化模块 }, shared: { '@angular/core': { singleton: true, strictVersion: true }, '@ngrx/store': { singleton: true, strictVersion: true }, '@angular/router': { singleton: true, strictVersion: true } } };
Shell侧顺序加载模块
// Shell/src/app/routes.ts import { inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { Router } from '@angular/router'; import { loadRemoteModule } from '@angular-architects/module-federation'; const routes = [ { path: 'todo', loadChildren: () => Promise.all([ // 先加载初始化模块 loadRemoteModule({ type: 'module', remoteEntry: 'http://localhost:4300/remoteEntry.js', exposedModule: './MfeInit', }), // 再加载业务模块 loadRemoteModule({ type: 'module', remoteEntry: 'http://localhost:4300/remoteEntry.js', exposedModule: './MySubModule', }) ]).then(([initModule, mfeModule]) => { const shellStore = inject(Store); const shellRouter = inject(Router); // 执行初始化 initModule.init(shellStore, shellRouter); return mfeModule.MySubModule; }), }, ];
注意事项
- 确保Shell与MFE的共享依赖(如NgRx、Router)版本一致,启用
singleton: true避免多实例冲突 - 所有初始化逻辑需添加容错处理,避免配置加载失败导致整个MFE无法使用
- 尽量减少MFE对Shell全局变量的依赖,优先通过函数参数传递必要依赖
内容的提问来源于stack exchange,提问作者Gnik
相关产品推荐
相关产品推荐

