Angular 14 Module Federation单远程不可用致应用整体失效问题
多微前端Angular项目Module Federation v14迁移后远程端点不可用导致应用崩溃问题
我们的多微前端Angular项目基于Manfred Steyer的Module Federation库构建。从v12迁移到v14后出现异常:当某一远程端点不可用时,整个应用直接停止运行并显示空白页面;而在v12版本中,仅该不可用的微前端无法访问,其余功能可正常使用。
Shell应用Webpack配置
const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ name: 'shell', filename: 'shell.js', sharedMappings: ['frontend/mfe-shared'], exposes: { './Module': 'apps/shell/src/app/app.module.ts', }, remotes: { dashboard: `http://localhost:4201/dashboard.js`, offers: `http://localhost:4206/offers.js`, clients: `http://localhost:4206/clients.js`, }, shared: share({ '@angular/core': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/common': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/common/http': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/router': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, 'ngx-webstorage-service': { singleton: true, strictVersion: true }, }), });
Dashboard远程应用Webpack配置示例
const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ name: 'dashboard', filename: 'dashboard.js', sharedMappings: ['frontend/mfe-shared'], exposes: { './Module': 'apps/dashboard/src/app/app.module.ts', }, shared: share({ '@angular/core': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/common': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/common/http': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, '@angular/router': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }, 'ngx-webstorage-service': { singleton: true, strictVersion: true }, }), });
路由配置
const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('dashboard/Module').then(({ AppModule }) => AppModule), }, { path: 'client', loadChildren: () => import('partner/Module').then(({ AppModule }) => AppModule), }, { path: 'offers', loadChildren: () => import('iam/Module').then(({ IamModule }) => IamModule), } ]; @NgModule({ imports: [ RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy', paramsInheritanceStrategy: 'always' }), ], exports: [RouterModule], }) export class AppRoutingModule {}
使用版本
- Angular 14.2.12
- @angular-architects/module-federation: 14.3.14
问题原因与修复方案
Module Federation v14默认会在应用初始化阶段尝试加载所有配置的远程模块,一旦某个远程不可用,会直接中断应用初始化流程,导致空白页面。要恢复v12的容错性,需做以下调整:
动态加载远程模块,避免初始化时预加载
修改Shell的Webpack配置,将remotes改为动态导入的方式,仅在实际需要时加载对应远程模块:remotes: { dashboard: () => import('@angular-architects/module-federation/webpack').then(m => m.loadRemote({ type: 'module', remoteEntry: 'http://localhost:4201/dashboard.js', exposedModule: './Module', remoteName: 'dashboard' })), offers: () => import('@angular-architects/module-federation/webpack').then(m => m.loadRemote({ type: 'module', remoteEntry: 'http://localhost:4206/offers.js', exposedModule: './Module', remoteName: 'offers' })), clients: () => import('@angular-architects/module-federation/webpack').then(m => m.loadRemote({ type: 'module', remoteEntry: 'http://localhost:4206/clients.js', exposedModule: './Module', remoteName: 'clients' })), },为路由加载添加错误捕获
创建一个备用的错误模块(用于显示微前端不可用的提示),然后在路由的loadChildren中添加catch逻辑,当远程模块加载失败时跳转至该模块:// 备用错误模块示例 import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { MfeUnavailableComponent } from './mfe-unavailable/mfe-unavailable.component'; const errorRoutes: Routes = [ { path: '', component: MfeUnavailableComponent } ]; @NgModule({ imports: [RouterModule.forChild(errorRoutes)], exports: [RouterModule] }) export class MfeErrorModule {} // 修改后的路由配置 const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('dashboard/Module') .then(({ AppModule }) => AppModule) .catch(() => import('./mfe-error/mfe-error.module').then(m => m.MfeErrorModule)), }, { path: 'client', loadChildren: () => import('partner/Module') .then(({ AppModule }) => AppModule) .catch(() => import('./mfe-error/mfe-error.module').then(m => m.MfeErrorModule)), }, { path: 'offers', loadChildren: () => import('iam/Module') .then(({ IamModule }) => IamModule) .catch(() => import('./mfe-error/mfe-error.module').then(m => m.MfeErrorModule)), } ];常规检查:共享依赖配置
确认共享依赖的strictVersion和requiredVersion配置符合项目实际情况,避免因依赖版本不匹配引发额外加载问题。
内容的提问来源于stack exchange,提问作者Francesco
相关产品推荐
相关产品推荐

