1. 项目结构
microfrontend-demo/
├── shell/ # 主应用 (Shell)
├── mfe1/ # 微前端应用1
├── mfe2/ # 微前端应用2
└── shared/ # 共享库
2. 主应用 (Shell) 配置
shell/webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
module.exports = {
output: {
uniqueName: "shell",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
plugins: [
new ModuleFederationPlugin({
remotes: {
"mfe1": "mfe1@http://localhost:4201/remoteEntry.js",
"mfe2": "mfe2@http://localhost:4202/remoteEntry.js"
},
shared: {
"@angular/core": { singleton: true, strictVersion: true },
"@angular/common": { singleton: true, strictVersion: true },
"@angular/router": { singleton: true, strictVersion: true },
"@angular/common/http": { singleton: true, strictVersion: true },
// 其他共享库...
}
})
]
};
shell/src/app/app-routing.module.ts
使用:RouterModule.forRoot(routes)
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
redirectTo: 'home'
},
{
path: 'home',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
},
{
path: 'mfe1',
loadChildren: () => import('mfe1/Module').then(m => m.Mfe1Module)
},
{
path: 'mfe2',
loadChildren: () => import('mfe2/Module').then(m => m.Mfe2Module)
},
{
path: '**',
redirectTo: 'home'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
shell/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
RouterModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
3. 微前端应用1 (MFE1) 配置
mfe1/webpack.config.js
只需导出包含所有路由配置的Module即可
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
module.exports = {
output: {
uniqueName: "mfe1",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
plugins: [
new ModuleFederationPlugin({
name: "mfe1",
filename: "remoteEntry.js",
exposes: {
'./Module': './src/app/mfe1/mfe1.module.ts',
},
shared: {
"@angular/core": { singleton: true, strictVersion: true },
"@angular/common": { singleton: true, strictVersion: true },
"@angular/router": { singleton: true, strictVersion: true },
// 其他共享库...
}
})
]
};
mfe1/src/app/mfe1/mfe1.module.ts
使用 RouterModule.forChild(routes)
时,若未将所有路由配置集中在一个模块中,则需在每个组件内单独添加 RouterModule.forChild([{path:'',component:''}])
配置。
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { Mfe1Component } from './mfe1.component';
const routes: Routes = [
{ path: '', component: Mfe1Component },
{ path: 'details', loadChildren: () => import('./details/details.module').then(m => m.DetailsModule) }
];
@NgModule({
declarations: [Mfe1Component],
imports: [
CommonModule,
RouterModule.forChild(routes)
]
})
export class Mfe1Module { }
4. 微前端应用2 (MFE2) 配置
mfe2/webpack.config.js
只需导出包含所有路由配置的Module即可
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const mf = require("@angular-architects/module-federation/webpack");
const path = require("path");
module.exports = {
output: {
uniqueName: "mfe2",
publicPath: "auto"
},
optimization: {
runtimeChunk: false
},
plugins: [
new ModuleFederationPlugin({
name: "mfe2",
filename: "remoteEntry.js",
exposes: {
'./Module': './src/app/mfe2/mfe2.module.ts',
},
shared: {
"@angular/core": { singleton: true, strictVersion: true },
"@angular/common": { singleton: true, strictVersion: true },
"@angular/router": { singleton: true, strictVersion: true },
// 其他共享库...
}
})
]
};
mfe2/src/app/mfe2/mfe2.module.ts
使用 RouterModule.forChild(routes)
时,若未将所有路由配置集中在一个模块中,则需在每个组件内单独添加 RouterModule.forChild([{path:'',component:''}])
配置。
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { Mfe2Component } from './mfe2.component';
const routes: Routes = [
{ path: '', component: Mfe2Component },
{ path: 'settings', loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule) }
];
@NgModule({
declarations: [Mfe2Component],
imports: [
CommonModule,
RouterModule.forChild(routes)
]
})
export class Mfe2Module { }
5. 共享库配置
shared/package.json
{
"name": "shared",
"version": "1.0.0",
"peerDependencies": {
"@angular/core": "^15.0.0",
"@angular/common": "^15.0.0",
"@angular/router": "^15.0.0"
}
}
6. 导航组件示例
shell/src/app/nav/nav.component.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-nav',
template: `
<nav>
<button (click)="navigateTo('home')">Home</button>
<button (click)="navigateTo('mfe1')">MFE1</button>
<button (click)="navigateTo('mfe2')">MFE2</button>
</nav>
`
})
export class NavComponent {
constructor(private router: Router) {}
navigateTo(route: string): void {
this.router.navigate([route]);
}
}
7. 环境配置
shell/src/environments/environment.ts
export const environment = {
production: false,
mfe1Url: 'http://localhost:4201/remoteEntry.js',
mfe2Url: 'http://localhost:4202/remoteEntry.js'
};
8. 启动脚本
在 package.json 中添加并行启动脚本:
"scripts": {
"start": "npm-run-all --parallel start:shell start:mfe1 start:mfe2",
"start:shell": "ng serve shell -o --port 4200",
"start:mfe1": "ng serve mfe1 -o --port 4201",
"start:mfe2": "ng serve mfe2 -o --port 4202"
}