微前端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. 适合松耦合异步通信
相关推荐
华科易迅1 小时前
人工智能学习38-VGG训练
人工智能·学习·人工智能学习38-vgg训练
&白帝&2 小时前
前端实现截图的几种方法
前端
蓝胖子不会敲代码2 小时前
跟着AI学习C# Day27
开发语言·学习·c#
动能小子ohhh2 小时前
html实现登录与注册功能案例(不写死且只使用js)
开发语言·前端·javascript·python·html
小小小小宇2 小时前
大文件断点续传笔记
前端
Jimmy3 小时前
理解 React Context API: 实用指南
前端·javascript·react.js
保持学习ing3 小时前
SpringBoot电脑商城项目--显示勾选+确认订单页收货地址
java·前端·spring boot·后端·交互·jquery
李明一.3 小时前
Java 全栈开发学习:从后端基石到前端灵动的成长之路
java·前端·学习
观默4 小时前
我用AI造了个“懂我家娃”的育儿助手
前端·人工智能·产品