使用 AWS Amplify、Angular 和 Module Federation 为微前端创建门户
Milena Godau 和 Pedro Garcia,Amazon Web Services
摘要
通过微前端架构,多个团队能够独立处理前端应用程序的不同部分。每个团队都可以开发、构建和部署前端的某个片段,而不会干扰应用程序的其他部分。从最终用户的角度来看,它似乎就是一个单一的、有凝聚力的应用程序。但是,他们正在与由不同团队发布的多个独立应用程序进行交互。
本文档介绍了如何使用 AWS Amplify、Angular 前端框架和 Module Federation 来创建微前端架构。在此模式中,微前端通过 Shell(或父)应用程序在客户端进行组合。Shell 应用程序可充当检索、显示和集成微前端的容器。Shell 应用程序可处理全局路由,以便加载不同的微前端。@angular-architects/module-federation 插件可将 Module Federation 与 Angular 集成。您可以通过使用 AWS Amplify 部署 Shell 应用程序和微前端。最终用户可通过基于 Web 的门户访问应用程序。
该门户是垂直拆分的。这意味着这些微前端是整个视图或视图组,而不是同一视图的一部分。因此,Shell 应用程序一次只能加载一个微前端。
微前端是以远程模块的形式实施的。Shell 应用程序会延迟加载这些远程模块,因此将微前端初始化推迟到需要时再执行。这种方法通过仅加载必要的模块来优化应用程序性能。这就可以减少初始加载时间,并改善整体用户体验。此外,您可以通过 webpack 配置文件(webpack.config.js),跨模块共享常见的依赖项。这种做法可以促进代码重复使用,减少重复并简化捆绑过程。
先决条件和限制
先决条件
一个活跃的 AWS 账户
Node.js and npm,已安装
Amplify CLI,已安装
Angular CLI,已安装
使用 AWS Amplify 的权限
熟悉 Angular
产品版本
限制
微前端架构是一种构建具有弹性的可扩展 Web 应用程序的强大方法。但是,在采用这种方法之前,请务必了解以下潜在挑战:
集成 – 与整体式前端相比,其中一个关键挑战是复杂性可能会增加。编排多个微前端、处理它们之间的通信以及管理共享依赖项可能会更加复杂。此外,微前端之间的通信可能会产生性能开销。这种通信会增加延迟并降低性能。这一问题需要通过高效消息收发机制和数据共享策略来解决。
代码重复 – 由于每个微前端都是独立开发的,因此通用功能或共享库可能存在代码重复风险。这可能会增加应用程序的总体大小,并带来维护挑战。
协调和管理 – 跨多个微前端协调开发和部署流程可能具有挑战性。在分布式架构中,确保版本控制一致、管理依赖项以及维护组件之间的兼容性变得更为重要。建立明确的治理、指导原则以及自动测试和部署管道,对于实现无缝协作和交付至关重要。
测试 – 测试微前端架构可能比测试整体式前端更为复杂。要执行跨组件集成测试和端到端测试,并验证跨多个微前端的一致用户体验,需要付出额外的努力以及专门的测试策略。
在承诺使用微前端方法之前,我们建议您先查看了解和实施 AWS 上的微前端。
架构
在微前端架构中,每个团队都可独立开发和部署功能。下图显示了多个 DevOps 团队如何协同工作。门户团队开发 Shell 应用程序。Shell 应用程序则可充当容器,检索、显示和集成其他 DevOps 团队发布的微前端应用程序。您使用 AWS Amplify 来发布 Shell 应用程序和微前端应用程序。
架构图显示了以下工作流程:
门户团队开发和维护 Shell 应用程序。Shell 应用程序编排微前端的集成和渲染,从而构成整个门户。
团队 A 和 B 开发并维护一个或多个集成到门户中的微前端或功能。每个团队都可以独立处理其各自的微前端。
最终用户使用 Amazon Cognito 进行身份验证。
最终用户访问门户,然后加载 Shell 应用程序。当用户导航时,Shell 应用程序会处理路由并检索请求的微前端,加载其捆绑包。
AWS 服务
其他工具
代码存储库
此模式的代码可在使用 Angular 和 Module Federation 的微前端门户 GitHub 存储库中获得。此存储库包含以下两个文件夹:
最佳实践
微前端架构具有诸多优势,但也会带来复杂性。以下是有助于实现流畅开发、高质量代码和出色用户体验的一些最佳实践:
规划和沟通 – 为了简化协作,请在前期规划、设计和清晰的沟通渠道等方面投入时间和精力。
设计一致性 – 通过使用设计系统、样式指南和组件库,在所有微前端中强制实施一致的视觉风格。这样便能提供连贯的用户体验并加快开发速度。
依赖项管理 – 由于微前端会独立演变发展,因此应采用标准化合同和版本控制策略,以便有效管理依赖项并防止兼容性问题。
微前端架构 – 为了实现独立的开发和部署,每个微前端都应该对封装的功能承担清晰明确的责任。
集成和通信 – 为了促进顺利集成并最大限度地减少冲突,应在微前端之间定义明确的合同和通信协议,包括 API、事件和共享数据模型。
测试和质量保证 – 为微前端实施测试自动化和持续集成管道。这样可以提高整体质量,减少手动测试工作,并验证微前端交互之间的功能。
性能优化 – 持续监控性能指标并跟踪微前端之间的依赖关系。这可以帮助您识别瓶颈并保持最佳的应用程序性能。为此,请使用性能监控和依赖关系分析工具。
开发人员体验 – 注重开发人员体验,提供清晰的文档、工具和示例。这可以帮助您简化开发流程及吸引新的团队成员。
操作说明
| 任务 | 描述 | 所需技能 |
|---|
创建 Shell 应用程序。 | 在 Angular CLI 中,输入以下命令: ng new shell --routing
输入以下命令,以导航至项目文件夹: cd shell
Shell 和微前端应用程序的文件夹和项目结构可以完全彼此独立。它们可以作为独立的 Angular 应用程序来进行处理。
| 应用程序开发人员 |
安装 插件。 | 在 Angular CLI 中,输入以下命令,以安装 @angular-architects/module-federation 插件: ng add @angular-architects/module-federation --project shell --port 4200
| 应用程序开发人员 |
将微前端 URL 添加为环境变量。 | 打开 environment.ts 文件。 将 mfe1URL: 'http://localhost:5000' 添加到 environment 对象: export const environment = {
production: false,
mfe1URL: 'http://localhost:5000',
};
保存并关闭 environment.ts 文件。
| 应用程序开发人员 |
定义路由。 | 打开 app-routing.module.ts 文件。 在 Angular CLI 中,输入以下命令,以从 @angular-architects/module-federation 插件导入 loadRemoteModule 模块: import { loadRemoteModule } from '@angular-architects/module-federation';
将默认路由设置为以下内容: {
path: '',
pathMatch: 'full',
redirectTo: 'mfe1'
},
设置微前端的路由: {
path: 'mfe1',
loadChildren: () => loadRemoteModule({
type: 'module',
remoteEntry: `${environment.mfe1URL}/remoteEntry.js`,
exposedModule: './Module'
})
.then(m => m.Mfe1Module)
},
保存并关闭 app-routing.module.ts 文件。
| 应用程序开发人员 |
声明 mfe1 模块。 | 在 src 文件夹中,创建一个名为 decl.d.ts 的新文件。 打开 decl.d.ts 文件。 将以下内容添加到文件中: declare module 'mfe1/Module';
保持并关闭 decl.d.ts 文件。
| 应用程序开发人员 |
准备微前端的预加载。 | 预加载微前端有助于 webpack 正确协商共享库和程序包。 打开 main.ts 文件。 将该内容替换为以下内容: import { loadRemoteEntry } from '@angular-architects/module-federation';
Promise.all([
loadRemoteEntry(`${environment.mfe1URL}/remoteEntry.js`, 'mfe1'),
])
.catch(err => console.error('Error loading remote entries', err))
.then(() => import('./bootstrap'))
.catch(err => console.error(err));
保持并关闭 main.ts 文件。
| 应用程序开发人员 |
调整 HTML 内容。 | 打开 app.component.html 文件。 将该内容替换为以下内容: <h1>Shell application is running!</h1>
<router-outlet></router-outlet>
保存并关闭 app.component.html 文件。
| 应用程序开发人员 |
| 任务 | 描述 | 所需技能 |
|---|
创建微前端。 | 在 Angular CLI 中,输入以下命令: ng new mfe1 --routing
输入以下命令,以导航至项目文件夹: cd mfe1
| 应用程序开发人员 |
安装 插件。 | 输入以下命令,以安装 @angular-architects/module-federation 插件: ng add @angular-architects/module-federation --project mfe1 --port 5000
| 应用程序开发人员 |
创建模块和组件。 | 输入以下命令,以创建模块和组件并将其导出为远程条目模块: ng g module mfe1 --routing
ng g c mfe1
| 应用程序开发人员 |
设置默认路由路径。 | 打开 mfe-routing.module.ts 文件。 将默认路由设置为以下内容: {
path: '',
component: Mfe1Component
},
保存并关闭 mfe-routing.module.ts 文件。
| 应用程序开发人员 |
添加 mfe1 路由。 | 打开 app-routing.module.ts 文件。 将默认路由设置为以下内容: {
path: '',
pathMatch: 'full',
redirectTo: 'mfe1'
},
添加以下 mfe1 路由: {
path: 'mfe1',
loadChildren: () =>
import('./mfe1/mfe1.module').then((m) => m.Mfe1Module),
},
保存并关闭 app-routing.module.ts 文件。
| 应用程序开发人员 |
编辑 webpack.config.js 文件。 | 打开 webpack.config.js 文件。 编辑 For remotes 部分以匹配以下内容: // For remotes (please adjust)
name: "mfe1",
filename: "remoteEntry.js",
exposes: {
'./Module': './src/app/mfe1/mfe1.module.ts',
},
在 shared 部分中,添加 mfe1 应用程序与 Shell 应用程序共享的所有依赖项: shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
保持并关闭 webpack.config.js 文件。
| 应用程序开发人员 |
调整 HTML 内容。 | 打开 app.component.html 文件。 将该内容替换为以下内容: <router-outlet></router-outlet>
保存并关闭 app.component.html 文件。
| 应用程序开发人员 |
| 任务 | 描述 | 所需技能 |
|---|
运行 mfe1 应用程序。 | 输入以下命令,以启动 mfe1 应用程序: npm start
在 Web 浏览器中,访问 http://localhost:5000。 验证微前端是否可以独立运行。mfe1 应用程序应该能够正确渲染,不会出现任何错误。
| 应用程序开发人员 |
运行 Shell 应用程序。 | 输入以下命令,以启动 Shell 应用程序: npm start
在 Web 浏览器中,访问 http://localhost:4200/mfe1。 验证 mfe1 微前端是否已嵌入 Shell 应用程序中。门户应用程序应该能够正确渲染,不会出现任何错误,并且 mfe1 应用程序应嵌入其中。
| 应用程序开发人员 |
| 任务 | 描述 | 所需技能 |
|---|
创建模块和组件。 | 在 Shell 应用程序的根文件夹中,输入以下命令,以创建错误页面的模块和组件: ng g module error-page --routing
ng g c error-page
| 应用程序开发人员 |
调整 HTML 内容。 | 打开 error-page.component.html 文件。 将该内容替换为以下内容: <p>Sorry, this page is not available.</p>
保存并关闭 error-page.component.html 文件。
| 应用程序开发人员 |
设置默认路由路径。 | 打开 error-page-routing.module.ts 文件。 将默认路由设置为以下内容: {
path: '',
component: ErrorPageComponent
},
保存并关闭 error-page-routing.module.ts 文件。
| 应用程序开发人员 |
创建函数以加载微前端。 | 打开 app-routing.module.ts 文件。 创建以下函数: function loadMFE(url: string) {
return loadRemoteModule({
type: 'module',
remoteEntry: `${url}/remoteEntry.js`,
exposedModule: './Module'
})
.then(m => m.Mfe1Module)
.catch(
() => import('./error-page/error-page.module').then(m => m.ErrorPageModule)
);
}
将 mfe1 路由修改为以下内容: {
path: 'mfe1',
loadChildren: () => loadMFE(environment.mfe1URL)
},
保存并关闭 app-routing.module.ts 文件。
| 应用程序开发人员 |
测试错误处理。 | 如果它尚未运行,请输入以下命令,以启动 Shell 应用程序: npm start
在 Web 浏览器中,访问 http://localhost:4200/mfe1。 验证错误页面是否已渲染。您应该会看到以下文本: Sorry, this page is not available.
| 应用程序开发人员 |
| 任务 | 描述 | 所需技能 |
|---|
部署微前端。 | 在 Amplify CLI 中,导航到微前端应用程序的根文件夹。 输入以下命令,以初始化 Amplify: amplify init
当提示您输入 Amplify 项目的名称时,请按 Enter 键。这样一来,就能重复使用 package.json 文件中的名称。 当提示您使用上述配置初始化项目时,请输入 Yes。 当提示您选择身份验证方法时,请选择 AWS Profile。 选择要使用的配置文件。 等待 Amplify 初始化项目。此过程完成后,您将在终端中收到一条确认消息。 输入以下命令,以将 Amplify 托管类别添加到微前端: amplify add hosting
当提示您选择插件模块时,请选择 Hosting with Amplify Console。 当提示您选择类型时,请选择 Manual deployment。 输入以下命令,以安装项目 npm 依赖项: npm install
输入以下命令,以将应用程序发布到 Amplify 控制台: amplify publish -y
发布完成后,Amplify 会返回微前端的 URL。 复制 URL。更新 Shell 应用程序时,您需要用到此值。
| 应用程序开发人员、AWS DevOps |
部署 Shell 应用程序。 | 在 src/app/environments 文件夹中,打开 environments.prod.ts 文件。 将 mfe1URL 值替换为已部署的微前端的 URL: export const environment = {
production: true,
mfe1URL: 'https://<env>.<Amplify-app-ID>.amplifyapp.com'
};
保存并关闭 environments.prod.ts 文件。 在 Amplify CLI 中,导航到 Shell 应用程序的根文件夹。 输入以下命令,以初始化 Amplify: amplify init
当提示您输入 Amplify 项目的名称时,请按 Enter 键。这样一来,就能重复使用 package.json 文件中的名称。 当提示您使用上述配置初始化项目时,请输入 Yes。 当提示您选择身份验证方法时,请选择 AWS Profile。 选择要使用的配置文件。 等待 Amplify 初始化项目。此过程完成后,您将在终端中收到一条确认消息。 将 Amplify 托管类别添加到 Shell 应用程序中: amplify add hosting
当提示您选择插件模块时,请选择 Hosting with Amplify Console。 当提示您选择类型时,请选择 Manual deployment。 输入以下命令,以安装项目 npm 依赖项: npm install
输入以下命令,以将 Shell 应用程序发布到 Amplify 控制台: amplify publish -y
发布完成后,Amplify 会返回已部署的 Shell 应用程序的 URL。 记下 Shell 应用程序的 URL。
| 应用程序开发人员、应用程序所有者 |
启用 CORS。 | 由于 Shell 和微前端应用程序独立托管在不同的域上,因此您必须在微前端启用跨源资源共享(CORS)。这允许 Shell 应用程序加载来自不同源的内容。要启用 CORS,请添加自定义标头。 在 Amplify CLI 中,导航到微前端的根文件夹。 输入以下命令: amplify configure hosting
当提示您配置自定义设置时,请输入 Y。 登录 AWS 管理控制台,然后打开 Amplify 控制台。 选择微前端。 在导航窗格中,选择托管,然后选择自定义标头。 选择编辑。 在编辑自定义标头窗口中,输入以下内容: customHeaders:
- pattern: '*.js'
headers:
- key: Access-Control-Allow-Origin
value: '*'
- key: Access-Control-Allow-Methods
value: 'GET, OPTIONS'
- key: Access-Control-Allow-Headers
value: '*'
选择保存。 重新部署微前端以应用新的自定义标头。
| 应用程序开发人员、AWS DevOps |
在 Shell 应用程序上创建重写规则。 | Angular shell 应用程序已配置为使用 HTML5 路由。如果用户执行硬刷新,Amplify 会尝试从当前 URL 加载页面。这便会生成 403 错误。为避免这种情况,您可以在 Amplify 控制台中添加一条重写规则。 要创建重写规则,请执行以下步骤: 在 Amplify CLI 中,导航到 Shell 应用程序的根文件夹。 输入以下命令: amplify configure hosting
当提示您配置自定义设置时,请输入 Y。 打开 Amplify 控制台。 选择 Shell 应用程序。 在导航窗格中,依次选择托管、重写和重定向。 在重写和重定向页面上选择管理重定向。 选择打开文本编辑器。 在 JSON 编辑器中,输入以下重新导向: [
{
"source": "/<*>",
"target": "/index.html",
"status": "404-200",
"condition": null
}
]
选择保存。
| 应用程序开发人员、AWS DevOps |
测试 Web 门户。 | 在 Web 浏览器中,输入已部署的 shell 应用程序的 URL。 验证 Shell 应用程序和微前端是否已正确加载。
| 应用程序开发人员 |
| 任务 | 描述 | 所需技能 |
|---|
删除应用程序。 | 如果您不再需要 Shell 和微前端应用程序,请将其删除。这有助于防止对您未使用的资源产生费用。 登录 AWS 管理控制台,然后打开 Amplify 控制台。 选择微前端。 在导航窗格中,选择应用程序设置,然后选择常规设置。 选择删除应用程序。 在确认窗口中,输入 delete,然后选择删除应用程序。 重复这些步骤,以删除 Shell 应用程序。
| 常规 AWS |
故障排除
| 问题 | 解决方案 |
|---|
运行 amplify init 命令时没有可用的 AWS 配置文件 | 如果您尚未配置 AWS 配置文件,您仍然可以继续执行 amplify init 命令。但是,当提示您选择身份验证方法时,您需要选择 AWS access keys 选项。准备好您的 AWS 访问密钥和私有密钥。 或者,您也可以为 AWS CLI 配置命名配置文件。有关说明,请参阅 AWS CLI 文档中的配置和凭证文件设置。 |
加载远程条目时出错 | 如果在 Shell 应用程序的 main.ts 文件中加载远程条目时遇到错误,请确认已正确设置 environment.mfe1URL 变量。此变量的值应为微前端的 URL。 |
访问微前端时出现 404 错误 | 如果您在尝试访问本地微前端(例如 http://localhost:4200/mfe1)时出现 404 错误,请检查以下内容: |
其他信息
AWS 文档
其他参考资料