微前端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. 适合松耦合异步通信
相关推荐
小磊哥er10 分钟前
【前端工程化】前端开发中如何做一套规范的项目模版
前端
一切顺势而行20 分钟前
vue总结2
学习
Wetoria21 分钟前
管理 git 分支时,用 merge 还是 rebase?
前端·git
前端开发与ui设计的老司机31 分钟前
UI前端与数字孪生融合新领域:智慧环保的污染源监测与治理
前端·ui
今天背单词了吗98036 分钟前
算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·学习·算法·排序算法·冒泡排序
Brookty43 分钟前
【操作系统】进程(二)内存管理、通信
java·linux·服务器·网络·学习·java-ee·操作系统
一只小风华~44 分钟前
Web前端开发: :has功能性伪类选择器
前端·html·html5·web
java攻城狮k1 小时前
【跟着PMP学习项目管理】项目管理 之 成本管理知识点
经验分享·笔记·学习·产品经理
成遇1 小时前
Eslint基础使用
javascript·typescript·es6
Mr_Mao5 小时前
Naive Ultra:中后台 Naive UI 增强组件库
前端