Ama MFE 角 Utils (英语)
@ama-mfe/ng-utils 是一个角库,旨在精简微前端架构中的通信
使用 iframes .
这个软件包是建立在 Amadeus 微前端框架工具包
并提供了一套工具,包括帮助者、包装和服务,以便利无缝的融合
主机和嵌入式应用程序之间的互动.
关键特征包括:
- 连接: 连接到通信协议,并向已注册的应用程序发送消息.
- 导航: 处理主机和嵌入式应用程序之间的导航信息. 嵌入式应用程序可以使用
RoutingService使其内部路由与主机应用程序同步。 主机应用程序可以使用NavigationConsumerService获取嵌入式应用程序中导航事件的通知. - 导航退出:当嵌入式模块持有未保存的工作时,谈判导航. 模块报告其块状状态,在导航开始前,任何一方都要求确认.
- 主题:允许在嵌入式模块中应用统一的CSS变量和样式,确保一致性 看着和感觉.
- 调整大小: 动态调整iframe 维度以适应嵌入式应用程序的内容,增强 用户体验.
- 用户活动: 跟踪用户在嵌入式微前端的交互,用于会话超时功能,分析,或任何需要检测用户活动的功能.
- Iframe 内嵌:即用即用Angular组件,使有所有 MFE 集成指令的iframe预先接通(连接,可缩放,主机信息,主题).
安装
要安装软件包, 请运行 :
npm exec ng add @ama-mfe/ng-utils
消费者和生产者
在通信协议中,一个消息由 Producer 并送交 Consumer 将读取信件
并根据其内容作出反应.
这个 @ama-mfe/ng-utils 软件包将基于主机应用程序与其软件交换的信息的一组特性曝光
嵌入式模块。 对于每个特性,软件包提供 Producer编号 :Consumer 双人.
前者负责根据触发器发送信息(调整事件规模,调用公共功能)
等),而后者则执行特性背后的逻辑(iframe的调整,一个主题的应用等).
如何使用
连接到通信服务
配置您的应用程序连接
应用程序首先需要提供和配置 ConnectionService 使用通讯协议.
这个 provideConnection 方法允许应用程序使用其他人将使用的唯一ID进行注册
连接并瞄准应用程序.
import {provideConnection} from '@ama-mfe/ng-utils';
export const appConfig: ApplicationConfig = {
providers: [
provideConnection({
id: 'applicationUniqueID'
})
]
};
启动与主机应用程序的通信
嵌入到另一个应用程序中的应用程序可以使用 [Connection Service] 连接到其主机. 要建立连接,嵌入式应用程序需要主机应用程序 ID (通过 连接服务提供商) (中文(简体) ).
// main.ts
import {inject, runInInjectionContext} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';
import {ConnectionService, NavigationConsumerService} from '@ama-mfe/ng-utils';
bootstrapApplication(App, appConfig)
.then((m) => {
runInInjectionContext(m.injector, () => {
if (window.top !== window.self) {
// If embedded in an iframe, connect to the host
inject(ConnectionService).connect('hostUniqueID');
}
// Other injections
})
});
启动与嵌入模块的连接
建议嵌入模块的方式是使用 IframeEmbedComponent,哪个
预线 connect, (中文(简体) ), scalable, (中文(简体) ), hostInfo 财务报告和审定财务报表 applyTheme 在iframe上:
<mfe-iframe-embed
src="https://my-embedded-app.example.com"
moduleId="myModuleUniqueID"
hostApplicationId="hostUniqueID" />
或者,如果你需要完全控制 Iframe,你可以使用 connect 直接指令.
一旦iframe被摧毁,通信管就会被关闭.
<iframe [src]='myModuleUrl'
[connect]='myModuleUniqueID'>
</iframe>
在这个例子中, myModuleUniqueID 指: 编号 provideConnection 方法.
启用基于消息的特性
要使用基于信件通信协议的特性,首先需要确定您的应用程序是否是
信件的用户 (Consumer或发送信息的人(Producer) (中文(简体) ).
这可能取决于信息的背景和类型。 例如,应用程序可以是导航的消费者
消息,但主题消息的制作者.
消费者
如果你是消息的消费者,请打电话 start 财务报告和审定财务报表 stop 分别启用和禁用特性的方法.
import {Component, inject} from '@angular/core';
import {NavigationConsumerService} from '@ama-mfe/ng-utils';
import {ThemeConsumerService} from "./theme-consumer-service";
@Component({
selector: 'app-example-module-component',
template: './example-module-component.html',
styleUrl: './example-module-component.scss',
})
export class ExampleModuleComponent {
private readonly navigationConsumerService = inject(NavigationConsumerService);
constructor() {
this.navigationConsumerService.start();
}
ngOnDestroy() {
this.navigationConsumerService.stop()
}
}
根据您的使用情况, 您可能需要在应用程序启动后立即启动服务 .
在这种情况下,你可以注射到 main.ts数字 :
// main.ts
import {inject, runInInjectionContext} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';
import {ConnectionService, ThemeConsumerService} from '@ama-mfe/ng-utils';
bootstrapApplication(App, appConfig)
.then((m) => {
runInInjectionContext(m.injector, () => {
if (window.top !== window.self) {
// If embedded in an iframe, connect to the host
inject(ConnectionService).connect('hostUniqueID');
// Start the service to consume messages
inject(ThemeConsumerService).start();
}
// Other injections
})
});
生产商
如果您的应用程序是制作人,只需注入消息制作服务,并在需要时拨打触发器. 触发消息的方法名称没有标准化. 每个礼拜都会不同.
以 @ama-mfe/ng-utils提供
您可自行查找每个服务的更多信息 README.md数字 :
写你自己的生产者和消费者.
使用 ProducerManagerService 编辑 ConsumerManagerService 支持您的自定义消息.
电文应该按其类型和版本加以识别,以便允许主机和主机之间有不同的电文版本 嵌入式应用程序(并避免迁移问题).
import type {Message} from '@amadeus-it-group/microfrontends';
export interface CustomMessageV1_0 extends Message {
type: 'custom',
version: '1.0',
// Custom properties
customPayload: any
}
// Use union type here to add all the future version
// For example CustomMessage = CustomMessageV1_0 | CustomMessagev2_0
export type CustomMessageVersions = CustomMessageV1_0;
信件版本兼容性
这个 ConsumerManagerService 以相容回落方式发送信件, 统一适用于每个
此软件包中的消息类型(导航、主题、大小调整、用户活动、历史和自定义消息).
在主要版本中,使用消费者最高的申报未成年人 ——即将到来的未成年人。 这样可以让制作者添加
在新小版本中的可选字段(例如). v1.1而不打破只执行的消费者 v1.0 年长者
处理器运行并简单地忽略未知的字段 .
主要部分是孤立的——没有在两个方向上出现跨重大倒退:
- 接入的 v2.x 永远不会回落到 v1.x 处理器( 跨主机的破解变化预期) .
- 消费者只声明v2.x将无法匹配接入的v1.x(消费者领先于生产者).
两个跨重大案件都导致 version_mismatch 错误。 因此,采用新的主要版本需要协调
生产者和消费者方面的最新情况.
消费者
消费者应实施 MessageConsumer 接口并输入 ConsumeManagerService 处理
向通信协议注册.
它应列出所支持的版本,并将其召回功能映射到 supportedVersions 公共对象.
import type {CustomMessageV1_0, CustomMessageVersions} from '@ama-mfe/messages';
import type {RoutedMessage} from '@amadeus-it-group/microfrontends';
import {DestroyRef, inject, Injectable} from '@angular/core';
import {ConsumerManagerService, type MessageConsumer} from '@ama-mfe/ng-utils';
@Injectable({
providedIn: 'root'
})
export class CustomConsumerService implements MessageConsumer<CustomMessageVersions> {
/**
* The type of messages this service handles ('custom').
*/
public readonly type = 'custom';
/**
* The supported versions of theme messages and their handlers.
*/
public readonly supportedVersions = {
'1.0': (message: RoutedMessage<CustomMessageV1_0>) => console.log('Do some stuff with this message version', message)
};
private readonly consumerManagerService = inject(ConsumerManagerService);
constructor() {
this.start();
inject(DestroyRef).onDestroy(() => this.stop());
}
/**
* Starts the theme handler service by registering it into the consumer manager service.
*/
public start() {
this.consumerManagerService.register(this);
}
/**
* Stops the theme handler service by unregistering it from the consumer manager service.
*/
public stop() {
this.consumerManagerService.unregister(this);
}
}
使用 AbstractMessageConsumer
作为捷径,定制消费者可以延伸 AbstractMessageConsumer时,已输入 ConsumerManagerService, (中文(简体) ),
提供默认 start编号 :stop 执行,并在主机注入上下文被销毁时自动取消注册.
消费者是 没有 自动启动: 不定义构建器, 让应用程序启动
明确(通常通过 provideAppInitializer和上面的其他消费者一样.
@Injectable({ providedIn: 'root' })
export class CustomConsumerService extends AbstractMessageConsumer<CustomMessageVersions> {
public readonly type = 'custom';
public readonly supportedVersions = {
'1.0': (message: RoutedMessage<CustomMessageV1_0>) => { /* ... */ }
};
}
// app.config.ts
import {inject, provideAppInitializer} from '@angular/core';
import {CustomConsumerService} from './custom-consumer.service';
export const appConfig: ApplicationConfig = {
providers: [
provideAppInitializer(() => { inject(CustomConsumerService).start(); })
]
};
说明: 消费者
@ama-mfe/ng-utils(例如:.NavigationConsumerService, (中文(简体) ),ThemeConsumerService, (中文(简体) ),HistoryConsumerService, (中文(简体) ),ResizeConsumerService) 仍然从他们的构造器自动启动后向后 兼容性,但这是 已贬值 将在v15中被删除。 从你那开始 应用程序配置。 这个migration-v14_5示意图线start()给你打电话ng update。 。 。 .
制作人
生产商应执行 MessageProducer 接口并输入 ProducerManagerService 处理
向通信协议注册.
一旦连接,它就可以通过 MessagePeerService。 。 。 .
import type {CustomMessageV1_0, CustomMessageVersions} from '../messages';
import {MessagePeerService} from '@amadeus-it-group/microfrontends-angular';
import {inject, Injectable} from '@angular/core';
import {type MessageProducer, type ErrorContent, registerProducer} from '@ama-mfe/ng-utils';
@Injectable({
providedIn: 'root'
})
export class CustomService implements MessageProducer<CustomMessageVersions> {
private readonly messageService = inject(MessagePeerService<CustomMessageVersions>);
constructor() {
registerProducer(this);
}
public handleError(message: ErrorContent<CustomMessage_V1_0>): void {
// If available, use your own logger
console.error('Error in custom service message', message);
}
public postMessageAction(payload: any): void {
const messageV10 = {
type: 'custom',
version: '1.0',
customPayload: 'test'
} satisfies CustomMessageV1_0;
this.messageService.send(messageV10);
}
}
Iframe 嵌入组件
这个 IframeEmbedComponent 是一个随时可用的组成部分,它使一个框架具有所有MEF整合指令
预设. 它处理URL的消毒,跨帧通信(connect),自动重排(scalable, (中文(简体) ),
主机信息注入( i)hostInfo和主题applyTheme) (中文(简体) ).
import {IframeEmbedComponent} from '@ama-mfe/ng-utils';
@Component({
selector: 'app-host',
imports: [IframeEmbedComponent],
template: `
<mfe-iframe-embed
src="https://my-embedded-app.example.com"
moduleId="my-module-id"
hostApplicationId="host-app-id"
sandbox="allow-scripts allow-same-origin"
allow="camera; microphone" />
`
})
export class HostComponent {}
| 投入 | 类型 | 说明 |
|---|---|---|
src | string | SafeResourceUrl | Iframe 的 URL 。 平面字符串自动被消毒. |
moduleId | string | 嵌入式模块的独特标识符(由 connect 指令). |
hostApplicationId | string | 发送到嵌入模块的主机应用程序标识符通过 hostInfo。 。 。 . |
sandbox | string | Iframe sandbox 权限 。 默认为 'allow-scripts allow-same-origin'。 。 。 . |
allow | string | 框架 权限策略 (例如:. 'camera; microphone') (中文(简体) ). 默认为 '' (属性未设置). |
这个 sandbox 财务报告和审定财务报表 allow 属性在初始化时出于安全原因设定(运行时间变化被忽略).
主办单位信息
主机应用程序
主机应用程序可以使用URL中的参数向嵌入式应用程序发送信息.
这个 IframeEmbedComponent 通过它的 moduleId 财务报告和审定财务报表
hostApplicationId 投入——不需要额外设置.
如果你自己管理这个框架,你可以应用 hostInfo 直接管道 :
<iframe [src]="'myModuleUrl' | hostInfo: {hostId: 'host-app-id', moduleId: 'my-module-to-embed'}"></iframe>
这将增加 location.origin 和嵌入式应用程序的 URL 主机的应用程序 ID.
嵌入式应用程序
嵌入式应用程序可以使用注射符访问上一节所发送的数据:
import {inject} from '@angular/core';
import {getHostInfo, isEmbedded} from '@ama-mfe/ng-utils';
export class SomeClass {
private readonly hostInfo = getHostInfo();
doSomething() {
if (this.hostInfo.applicationId === 'app1') {
// Do something when embedded in app1
} else if (isEmbedded()) {
// Do something when embedded elsewhere
} else {
// Do something when standalone
}
}
}
主机信息存储在会话存储中,这样在iframe内部导航时就不会丢失.
浏览器历史处理
在使用iframes来嵌入应用程序时,浏览器历史可能由主页和嵌入的iframe共享. 举个例子 <iframe src="https://example.com" sandbox="allow-same-origin"> 将分享与主页相同的历史。 这可能导致在使用浏览器"后"和"前"按钮时出现出乎意料的行为.
为了避免这种情况 @ama-mfe/ng-utils 将禁止程序在 Iframe 中运行以改变浏览器历史 。 当连接配置使用时会发生 provideConection() 函数。 这将防止iframe能够使用 history.pushState 财务报告和审定财务报表 history.replaceState 方法。 。 .
用户活动跟踪
用户活动跟踪功能允许一个主机应用程序(shell)监测用户在嵌入式微前端之间的相互作用. 这对于执行会话超时功能,分析功能,或任何需要知道用户何时与应用程序积极互动的功能都是有益的.
详细的文档、配置选项和示例见 用户活动 README。 。 。 .
导航退出
在嵌入式模块持有未完成的工作(如脏表)时,导航退出功能会谈判导航. 壳体和模块启动的导航都覆盖在内,由壳体拥有确认UI,模块运行了壳体所要求的任何清理后再进行导航.
完整的信息合同、服务、警卫、流动和整合步骤见 导航退出 README。 。 。 .