Angular微前端:如何通过Webpack Module Federation在App2模态框中调用App1组件
在Webpack Module Federation微前端架构中,Angular跨应用在模态框加载组件的实现方法
一、配置App1(远程应用,暴露目标组件)
首先要让App1把需要共享的组件暴露出来,通过Module Federation的配置实现:
- 修改App1的
webpack.config.js(如果用@angular-architects/module-federation搭建的项目,该文件在项目根目录),在ModuleFederationPlugin中添加exposes字段,指定要暴露的组件路径:
// App1 webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; const { AngularFederationPlugin } = require('@angular-architects/module-federation'); module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'App1', filename: 'remoteEntry.js', // 固定的远程入口文件名 exposes: { // 键是给宿主应用调用的别名,值是组件的相对路径 './TargetComponent': './src/app/target/target.component.ts', }, shared: { // 共享Angular核心依赖,确保单例和版本一致 '@angular/core': { singleton: true, strictVersion: true }, '@angular/common': { singleton: true, strictVersion: true }, '@angular/router': { singleton: true, strictVersion: true }, // 其他需要跨应用共享的依赖(比如RxJS等) } }), new AngularFederationPlugin() ] };
- 确保要暴露的组件(比如
TargetComponent)是独立可复用的:模板、样式自包含,避免依赖App1独有的全局服务或样式;如果有依赖,要么把依赖加入shared配置,要么在App2中提供对应的服务实例。
二、配置App2(宿主应用,加载远程组件)
接下来在App2中配置远程应用的地址,实现组件的动态加载:
- 修改App2的
webpack.config.js,在ModuleFederationPlugin中添加remotes字段,指向App1的remoteEntry.js地址:
// App2 webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; const { AngularFederationPlugin } = require('@angular-architects/module-federation'); module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'App2', remotes: { // 格式:[远程应用别名]@[remoteEntry.js的访问地址] App1: 'App1@http://localhost:4200/remoteEntry.js', // 开发环境地址 }, shared: { // 和App1保持一致的共享依赖配置,避免版本冲突 '@angular/core': { singleton: true, strictVersion: true }, '@angular/common': { singleton: true, strictVersion: true }, '@angular/router': { singleton: true, strictVersion: true }, } }), new AngularFederationPlugin() ] };
生产环境部署时,把地址改成App1实际的部署域名(比如https://your-app1-domain.com/remoteEntry.js)。
- 在App2中创建一个远程组件的包装组件,用来异步加载App1的组件:
// App2/src/app/remote-wrapper/remote-wrapper.component.ts import { Component, OnInit, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; @Component({ selector: 'app-remote-wrapper', template: '<div #container></div>' // 用来渲染远程组件的容器 }) export class RemoteWrapperComponent implements OnInit { @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef; constructor(private cfr: ComponentFactoryResolver) {} async ngOnInit() { // 动态导入App1暴露的组件 const remoteComponentModule = await import('App1/TargetComponent'); const componentFactory = this.cfr.resolveComponentFactory(remoteComponentModule.TargetComponent); // 在容器中创建组件实例 const componentRef = this.container.createComponent(componentFactory); // 如果需要给远程组件传参或监听事件,在这里处理 // componentRef.instance.inputProperty = 'xxx'; // componentRef.instance.outputEvent.subscribe(() => { /* 处理逻辑 */ }); } }
记得在App2的模块中声明这个包装组件。
三、在App2的模态框中使用远程组件
假设App2用Angular Material的MatDialog作为模态框组件,步骤如下:
- 在需要触发模态框的组件中,注入
MatDialog,并打开包装组件作为模态框内容:
// App2/src/app/trigger-component/trigger-component.component.ts import { Component } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { RemoteWrapperComponent } from '../remote-wrapper/remote-wrapper.component'; @Component({ selector: 'app-trigger-component', template: '<button (click)="openRemoteModal()">打开App1组件模态框</button>' }) export class TriggerComponent { constructor(private dialog: MatDialog) {} openRemoteModal() { this.dialog.open(RemoteWrapperComponent, { width: '600px', height: '400px', // 其他模态框配置(比如数据传递等) }); } }
- 确保App2的模块中导入了
MatDialogModule和相关的Angular Material模块。
四、关键注意事项
- 依赖版本一致性:App1和App2的共享依赖(比如Angular核心包)版本必须一致,否则会出现运行时错误。
- 开发环境启动顺序:要先启动App1,再启动App2,确保App2能正常获取到App1的
remoteEntry.js。 - 组件通信:如果远程组件需要和App2交互,通过包装组件中转输入输出属性;复杂通信可以用共享的状态管理库(比如NgRx)或者自定义事件总线。
- 生产部署:确保App1的
remoteEntry.js地址配置正确,并且两个应用的CORS配置允许跨域访问(如果部署在不同域名下)。
内容的提问来源于stack exchange,提问作者saeed
相关产品推荐
相关产品推荐

