微前端MFE:(React 与 Angular)框架之间的通信方式

微前端(MFE, Micro Frontends) 中使用 CustomEvent 是非常常见的,尤其是在不同子应用(Micro Apps)之间通信 的时候。今天将以React 主应用 ↔ Angular 子应用 之间的通信进行示例


React 主应用 <-> Angular 子应用 用 mitt(事件总线) 通信示例


1. React 主应用

  • 通过 props 给 Angular 子应用传递数据和回调函数

  • 使用 mitt(事件总线)进行异步事件广播

javascript 复制代码
// React 主应用示例(App.jsx)

import React, { useState, useEffect } from 'react';
import mitt from 'mitt';

const eventBus = mitt();
window.eventBus = eventBus;

function loadAngularApp(props) {
  // 假设 Angular 应用挂载到 #angular-container
  // 并且 Angular 子应用暴露了全局启动函数 angularApp.mount
  window.angularApp.mount(document.getElementById('angular-container'), props);
}

export default function App() {
  const [message, setMessage] = useState('');

  useEffect(() => {
    // 监听来自子应用的消息
    eventBus.on('from-angular', (msg) => setMessage(msg));
    return () => eventBus.off('from-angular');
  }, []);

  const onNotifyFromReact = (msg) => {
    setMessage('React 接收到子应用消息:' + msg);
  };

  const propsForAngular = {
    user: { id: 1, name: 'ReactUser' },
    notifyParent: onNotifyFromReact,
  };

  useEffect(() => {
    loadAngularApp(propsForAngular);
  }, []);

  return (
    <div>
      <h1>React 主应用</h1>
      <p>消息:{message}</p>
      <div id="angular-container" />
      <button onClick={() => eventBus.emit('from-react', 'React 主动发消息')}>
        React 向 Angular 发送消息
      </button>
    </div>
  );
}

2. Angular 子应用

  • 暴露 mount 方法接收父应用传来的 props

  • 通过传入的回调 notifyParent 通知 React

  • 使用 window.eventBus 监听 React 发来的事件

javascript 复制代码
// Angular 子应用核心代码(app.component.ts + bootstrap)

import { Component, Input, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h2>Angular 子应用</h2>
    <p>接收的用户:{{ user?.name }}</p>
    <button (click)="notifyReact()">通知 React</button>
  `,
})
export class AppComponent implements OnDestroy {
  @Input() user: any;
  @Input() notifyParent!: (msg: string) => void;

  private onFromReact = (msg: string) => alert('Angular 收到 React 消息: ' + msg);

  ngOnInit() {
    window.eventBus.on('from-react', this.onFromReact);
  }

  notifyReact() {
    if (this.notifyParent) this.notifyParent('来自 Angular 的消息');
  }

  ngOnDestroy() {
    window.eventBus.off('from-react', this.onFromReact);
  }
}

// 暴露给主应用挂载用的方法
export function mount(container: HTMLElement, props: any) {
  const moduleRef = platformBrowserDynamic().bootstrapModule(AppModule);
  // 这里需要将 props 注入到 Angular 应用,比如用 InjectionToken 或服务
  // 简单示意:
  const appRef = moduleRef.injector.get(ApplicationRef);
  // 把 props 传给 AppComponent,具体实现因项目不同略有差异
  // 也可以通过全局变量或服务传递
  container.appendChild(document.createElement('app-root'));
  // 实际渲染交给 Angular
}

3. 关键点总结

方式 说明
props React 主应用传给 Angular 子应用用户数据和回调函数
回调函数 Angular 调用回调函数通知 React 主应用
mitt 事件总线 React 和 Angular 异步事件广播,支持多对多通信
挂载函数 Angular 通过暴露 mount(container, props) 给 React 调用

React 主应用 <-> Angular 子应用 用 CustomEvent 通信示例


✅ 在 MFE 中使用 CustomEvent 的意义:

微前端架构中,每个子应用通常是相互隔离独立运行 的。它们可能是由不同团队使用不同技术栈开发的,因此需要一种轻量、技术无关的通信机制 ,而 CustomEvent 就是其中一种最佳选择。


✳️ 典型用法:主应用和子应用之间通信

🔁 从子应用向主应用发送事件
javascript 复制代码
// 子应用中
const loginSuccessEvent = new CustomEvent('user-login', {
  detail: { username: 'alice', token: 'abc123' }
});

window.dispatchEvent(loginSuccessEvent);
🔄 主应用监听这个事件
javascript 复制代码
// 主应用中
window.addEventListener('user-login', (e) => {
  console.log('收到来自子应用的登录事件:', e.detail);
  // 可以更新全局状态、通知其他子应用等
});

📡 场景例子:

  • 用户登录事件广播:一个子应用登录成功,主应用收到后可同步状态到其他子应用。

  • 路由通知:当子应用内部路由变化,通知主应用做高亮或记录。

  • 数据共享:某子应用加载了某数据,广播出去给其他依赖它的应用使用。


完整示例:

CustomEvent 实现 React 主应用和 Angular 子应用之间通信,核心思路就是:

  • 一方通过 window.dispatchEvent(new CustomEvent('事件名', { detail: 数据 })) 触发事件

  • 另一方通过 window.addEventListener('事件名', callback) 监听事件并拿到数据


1. React 主应用发送事件,接收 Angular 反馈
javascript 复制代码
// React 主应用示例

import React, { useEffect, useState } from 'react';

export default function App() {
  const [message, setMessage] = useState('');

  useEffect(() => {
    // 监听 Angular 发送的 CustomEvent
    const handler = (event) => {
      setMessage('收到 Angular 消息: ' + event.detail);
    };
    window.addEventListener('from-angular', handler);

    return () => {
      window.removeEventListener('from-angular', handler);
    };
  }, []);

  // 发送事件给 Angular
  const sendMessageToAngular = () => {
    window.dispatchEvent(new CustomEvent('from-react', { detail: '你好,Angular!' }));
  };

  return (
    <div>
      <h1>React 主应用</h1>
      <button onClick={sendMessageToAngular}>发送消息给 Angular</button>
      <p>{message}</p>
      <div id="angular-container" />
    </div>
  );
}

2. Angular 子应用监听 React 事件,发送反馈
javascript 复制代码
// Angular 子应用核心代码(app.component.ts)

import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h2>Angular 子应用</h2>
    <button (click)="sendMessageToReact()">发送消息给 React</button>
  `,
})
export class AppComponent implements OnInit, OnDestroy {
  private fromReactHandler = (event: CustomEvent) => {
    alert('Angular 收到 React 事件: ' + event.detail);
  };

  ngOnInit() {
    window.addEventListener('from-react', this.fromReactHandler as EventListener);
  }

  ngOnDestroy() {
    window.removeEventListener('from-react', this.fromReactHandler as EventListener);
  }

  sendMessageToReact() {
    window.dispatchEvent(new CustomEvent('from-angular', { detail: '你好,React!' }));
  }
}

3. Angular 应用挂载给 React 主应用调用(简要示意)

注意下面有A方式B方式,可根据实际情况进行选择使用

javascript 复制代码
//A方式: angularApp/bootstrap.ts

export function mount(container: HTMLElement) {
  // 启动 Angular 应用,把它渲染到 container
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .then(() => {
      container.appendChild(document.createElement('app-root'));
    });
}


//B方式
import { enableProdMode } from "@angular/core";
import { environment } from "./environments/environment";
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import "zone.js";
import { AppModule } from "./app/app.module";

if (environment.production) {
  enableProdMode();
}

let appRef: any = null;

const mount = async () => {
  appRef = await platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.error(err));
};

const unmount = () => {
  if (appRef) {
    appRef.destroy();
    appRef = null;
  }
};

export { mount, unmount };

React 主应用调用时传入容器:

javascript 复制代码
//A方式
window.angularApp.mount(document.getElementById('angular-container'));

//B方式
  const [AngularComponent, setAngularComponent] = useState(null);
  let unmountFunction = null;

  useEffect(() => {
    const loadModule = async () => {
      const { mount, unmount } = await loadRemoteModule({ 
        type: 'module',  
        remoteEntry: 'http://localhost:4200/remoteEntry.js',
        remoteName: 'B方式',
        exposedModule: './ReactMfeModule'});

        unmountFunction = unmount;

        setTimeout(() => {
          setAngularComponent(mount);
          setTimeout(() => {
            dispatchReactHostAppData();//传递初始数据使用
          }, 1000)
        }, 200);
    };
   
    loadModule();

    return () => {
      if (unmountFunction) {
        unmountFunction();
      }
    };
  }, []);

总结

优点 注意事项
1. 无需共享库,框架无关 1. 事件名要唯一,防止冲突
2. 传递数据简单、灵活 2. 事件数据建议放在 detail 字段
3. 浏览器原生,性能不错 3. 调试时注意事件监听和解绑
4. 适合松耦合异步通信
相关推荐
电饭叔3 分钟前
Tkinter Button 括号内的核心参数详解
python·学习
一周七喜h28 分钟前
在Vue3和TypeScripts中使用pinia
前端·javascript·vue.js
摘星编程28 分钟前
OpenHarmony环境下React Native:DatePicker日期选择器
react native·react.js·harmonyos
weixin_3954489133 分钟前
main.c_cursor_0202
前端·网络·算法
橙露36 分钟前
NNG通信框架:现代分布式系统的通信解决方案与应用场景深度分析
运维·网络·tcp/ip·react.js·架构
摘星编程1 小时前
用React Native开发OpenHarmony应用:Calendar日期范围选择
javascript·react native·react.js
东东5161 小时前
基于vue的电商购物网站vue +ssm
java·前端·javascript·vue.js·毕业设计·毕设
MediaTea1 小时前
<span class=“js_title_inner“>Python:实例对象</span>
开发语言·前端·javascript·python·ecmascript
闵帆1 小时前
反演学习器面临的鸿沟
人工智能·学习·机器学习
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony “微光笔记”——在灵感消逝前,为思想点一盏灯
开发语言·javascript·flutter·ui·dart