微前端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. 适合松耦合异步通信
相关推荐
盛夏绽放32 分钟前
jQuery 知识点复习总览
前端·javascript·jquery
胡gh2 小时前
依旧性能优化,如何在浅比较上做文章,memo 满天飞,谁在裸奔?
前端·react.js·面试
壹Y.3 小时前
非线性规划学习笔记
学习·数学建模
大怪v3 小时前
超赞👍!优秀前端佬的电子布洛芬技术网站!
前端·javascript·vue.js
胡gh3 小时前
你一般用哪些状态管理库?别担心,Zustand和Redux就能说个10分钟
前端·面试·node.js
项目題供诗3 小时前
React学习(十二)
javascript·学习·react.js
无羡仙3 小时前
Webpack 背后做了什么?
javascript·webpack
艾莉丝努力练剑4 小时前
【C语言16天强化训练】从基础入门到进阶:Day 7
java·c语言·学习·算法
roamingcode4 小时前
Claude Code NPM 包发布命令
前端·npm·node.js·claude·自定义指令·claude code
码哥DFS4 小时前
NPM模块化总结
前端·javascript