微前端 - Native Federation使用完整示例
这是一个极简化的 Angular 使用@angular-architects/native-federation 插件的微前端示例,只包含一个主应用和一个远程应用。
完整示例展示
项目结构
federation-simple/
├── host-app/ # 主应用
└── remote-app/ # 远程应用
创建远程应用 (remote-app)
初始化项目
ng new remote-app --standalone --minimal --style=css --routing=false
cd remote-app
npm install @angular-architects/native-federation --save-dev
配置 Native Federation
ng add @angular-architects/native-federation --project remote-app --port 4201
创建远程组件
编辑 src/app/hello.component.ts:
import { Component } from '@angular/core';@Component({standalone: true,template: `<div style="border: 2px solid blue;padding: 20px;margin: 10px;border-radius: 5px;"><h2>Hello from Remote App!</h2><p>This component is loaded from the remote app</p></div>`
})
export class HelloComponent {}
配置暴露的模块
编辑 federation.config.js:
module.exports = {name: 'remoteApp',exposes: {'./Hello': './src/app/hello.component.ts'},shared: {'@angular/core': { singleton: true, strictVersion: true },'@angular/common': { singleton: true, strictVersion: true },'rxjs': { singleton: true, strictVersion: true }}
};
创建主应用 (host-app)
初始化项目
ng new host-app --standalone --minimal --style=css --routing=true
cd host-app
npm install @angular-architects/native-federation @angular-architects/module-federation-tools --save-dev
配置 Native Federation
ng add @angular-architects/native-federation --project host-app --port 4200
创建主页面
编辑 src/app/app.component.ts:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';@Component({standalone: true,imports: [CommonModule, RouterOutlet],template: `<div style="padding: 20px;"><h1>Host Application</h1><nav><a routerLink="/" style="margin-right: 15px;">Home</a><a routerLink="/remote">Load Remote Component</a></nav><router-outlet></router-outlet></div>`
})
export class AppComponent {}
创建远程组件加载页面
创建 src/app/remote.component.ts:
在这里使用了<app-remote-hello> 那这个怎么来的呢?请见下面的关键点说明第四点!!!
import { Component, OnInit } from '@angular/core';
import { loadRemoteModule } from '@angular-architects/module-federation';@Component({standalone: true,template: `<div style="margin-top: 20px;"><h2>Remote Component</h2><div *ngIf="loading">Loading remote component...</div><div *ngIf="error" style="color: red;">Failed to load remote component: {{error}}</div><ng-container *ngIf="!loading && !error"><app-remote-hello></app-remote-hello></ng-container></div>`
})
export class RemoteComponent implements OnInit {loading = true;error: string | null = null;async ngOnInit() {try {await loadRemoteModule({remoteEntry: 'http://localhost:4201/remoteEntry.js',remoteName: 'remoteApp',exposedModule: './Hello'});this.loading = false;} catch (err) {this.error = err instanceof Error ? err.message : String(err);this.loading = false;}}
}
配置路由
编辑 src/app/app.routes.ts:
import { Routes } from '@angular/router';
import { RemoteComponent } from './remote.component';export const APP_ROUTES: Routes = [{ path: 'remote', component: RemoteComponent,providers: [// 注册远程组件{provide: 'remote-hello',useValue: {remoteEntry: 'http://localhost:4201/remoteEntry.js',remoteName: 'remoteApp',exposedModule: './Hello',componentName: 'Hello'}}]},{ path: '**', redirectTo: 'remote' }
];
配置应用
编辑 src/app/app.config.ts:
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';
import { APP_ROUTES } from './app.routes';
import { RemoteComponent } from '@angular-architects/module-federation-tools';export const appConfig: ApplicationConfig = {providers: [provideRouter(APP_ROUTES),importProvidersFrom(RemoteComponent.forRemote({type: 'module',remoteEntry: 'http://localhost:4201/remoteEntry.js',exposedModule: './Hello',componentName: 'Hello'}))]
};
配置 federation
编辑 federation.config.js:
module.exports = {name: 'hostApp',remotes: {'remoteApp': 'http://localhost:4201/remoteEntry.js'},shared: {'@angular/core': { singleton: true, strictVersion: true },'@angular/common': { singleton: true, strictVersion: true },'@angular/router': { singleton: true, strictVersion: true },'rxjs': { singleton: true, strictVersion: true }}
};
运行应用
- 打开两个终端窗口
- 在第一个终端中运行远程应用
- 在第二个终端中运行主应用
- 访问应用
- 在主应用中,点击 "Load Remote Component" 链接将加载并显示远程组件
关键点说明
项目简单说明下面三点:
-
远程应用: remote中暴露一个了简单的HelloCompoent并且配置了共享的 Angular 核心库;
-
主应用: host中使用 了loadRemoteModule动态加载远程组件,同时通过路由导航到远程组件,并且配置了远程模块的引用;
-
共享依赖: remote项目和Host项目Angular 核心库和 RxJS 被标记为共享单例,并且确保了版本兼容性
-
<app-remote-hello> 这个selector怎么来的
app-remote-hello
是自定义元素,通过下面的方式创建和使用的:
组件名称的生成规则: 当使用 @angular-architects/module-federation-tools
的 RemoteComponent
时,组件名称会自动按照以下规则生成:
app-remote-<componentName>
app
是 Angular 默认的前缀remote
表示这是一个远程组件<componentName>
是你在配置中指定的组件名称(这里是hello
)
具体配置来源:在示例中,这个名称来源于下面几个地方的配置:
在 app.config.ts
中:
importProvidersFrom(RemoteComponent.forRemote({type: 'module',remoteEntry: 'http://localhost:4201/remoteEntry.js',exposedModule: './Hello',componentName: 'Hello' // ← 这里定义了基础名称})
)
在 remote.component.ts
中:
<app-remote-hello></app-remote-hello>
完整的名称转换过程:
- 你配置了
componentName: Hello
- 系统会自动转换为小写形式:
hello
- 加上前缀
app-remote-
形成最终标签名:app-remote-hello
如何自定义这个名称:如果想使用不同的标签名,可以这样修改
// 在 app.config.ts 中
RemoteComponent.forRemote({// ...其他配置componentName: 'MyCustomHello', // 自定义名称elementName: 'my-hello-element' // 自定义元素名(可选)
})// 然后在模板中使用
<my-hello-element></my-hello-element>
为什么能这样使用:这是因为 @angular-architects/module-federation-tools
在底层做了以下工作:
-
动态注册了一个新的 Angular 组件
-
将该组件定义为自定义元素(Custom Element)
-
自动处理了组件名称的转换
-
设置了与远程组件的连接
验证方法:如果你想确认这个组件是如何被注册的,可以在浏览器开发者工具中:
-
打开 Elements 面板
-
找到
<app-remote-hello>
元素 -
查看它的属性,会发现它是一个 Angular组件
-
-
Angular 组件会有特殊属性:
-
查看是否有
_nghost-*
和_ngcontent-*
这类 Angular 特有的属性 -
例如:
<app-remote-hello _ngcontent-abc="" _nghost-def="">
-
-
检查自定义元素定义:
-
在 Console 中输入:
document.querySelector('app-remote-hello').constructor.name
-
如果是 Angular 组件,通常会显示
HTMLElement
(因为 Angular 组件最终是自定义元素)
-
-
参考资料:
核心资源
-
@angular-architects/native-federation 官方文档
📖 GitHub 仓库 & 文档-
包含安装指南、配置选项和基本用法
-
-
Module Federation 概念解释
📖 Webpack 官方文档-
理解微前端的核心机制
-
教程文章
-
Angular 微前端完整指南
📖 Angular Architects 博客-
含代码示例和架构图
-
-
实战案例分步教程
📖 Dev.to 详细教程-
从零开始的实现步骤
-
视频资源
-
官方演示视频
▶️ YouTube 教程-
30分钟实战演示(Angular团队录制)
-
-
模块联邦深度解析
▶️ Webpack 官方频道-
底层原理讲解
-
扩展工具
-
模块联邦工具库
📦 npm @angular-architects/module-federation-tools-
简化动态加载的工具
-
-
微前端状态管理方案
📖 NgRx 集成指南-
跨应用状态管理建议
-
常见问题
-
共享依赖解决方案
❓ Stack Overflow 热门讨论-
版本冲突处理方案
-
-
生产环境部署指南
📖 Angular 部署文档-
包含微前端部署注意事项
-
示例代码库
-
官方示例项目:可直接运行的完整项目
💻 GitHub 代码库