在微前端架构中,处理子应用之间的通信通常不是直接通过 DOM 或原生 JavaScript来实现的,因为子应用被设计为独立运行并隔离在其自身的上下文中。然而,有一些策略和工具可以帮助在子应用之间建立通信。
1. 使用公共的事件总线(推荐1)
主应用可以提供一个公共的事件总线(Event Bus),子应用可以通过它来发布和订阅事件。
使用全局事件总线可能会导致事件命名冲突和难以追踪的问题,特别是在大型系统中。
// 主应用中的事件总线
class EventBus {
constructor() {
this.events = new Map();
}
on(eventName, callback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, []);
}
this.events.get(eventName).push(callback);
}
emit(eventName, data) {
if (this.events.has(eventName)) {
this.events.get(eventName).forEach(callback => callback(data));
}
}
}
// 暴露给子应用
window.eventBus = new EventBus();
// 子应用A
window.eventBus.on('messageFromB', (data) => {
console.log('Received from B:', data);
});
// 子应用B
window.eventBus.emit('messageFromB', { hello: 'world' });
2. 使用自定义的 props 或 props down, events up
主应用可以作为中间层,通过属性(props)向子应用传递数据,并通过事件监听来捕获子应用发出的通知。
3. 使用状态管理库
如果所有子应用都使用相同的状态管理库(如 Redux, Vuex 等),那么它们可以通过共享状态来通信。但这种方法需要主应用来协调状态的更新和同步。
直接在子应用之间共享Redux store是不推荐的,因为这违反了微前端的隔离原则。
4. 使用通信库(推荐2)
有些微前端框架提供了通信库,如 qiankun 的 initGlobalState 功能。
主应用(main app)
import { registerMicroApps, start, initGlobalState } from 'qiankun';
// 初始化状态管理
const initialState = {
user: null,
// ... 其他全局状态
};
//initGlobalState 返回的 actions 对象包含了 setGlobalState 和 onGlobalStateChange 两个方法,分别用于设置和监听全局状态的变化。
const actions = initGlobalState(initialState);
// 注册微应用
registerMicroApps([
{
name: 'app1',
entry: '//localhost:8081',
container: '#app1',
activeRule: '/app1',
props: { actions } // 在主应用中,通过 props 将 actions 传递给子应用。
},
// 其他微应用配置...
]);
// 启动 qiankun
start();
子应用(app1)
//在子应用中,你可以通过 props 接收 actions 对象,并通过它来触发和监听全局状态的变化。
// 子应用的入口文件(通常是 main.js 或 index.js)
export async function bootstrap() {
// ...
}
export async function mount(props) {
// 子应用通过 props.actions 访问 actions 对象,并使用它来触发和监听全局状态的变化。
const { actions } = props;
//监听全局状态变化的回调函数会在状态更新后执行,并接收新的状态和之前的状态作为参数。
actions.onGlobalStateChange((state, prev) => {
console.log(state, prev);
// 根据状态变化执行相应的操作
});
// 触发全局状态的变化
//setGlobalState 方法接收一个函数,该函数接收当前的全局状态作为参数,并返回新的全局状态。这种方式可以确保状态更新的原子性,避免在并发更新时产生冲突。
actions.setGlobalState(state => {
return {
...state,
user: { name: 'John Doe' } // 假设更新用户信息
};
});
// ... 渲染应用
}
//如果需要在子应用卸载时取消对全局状态变化的监听,可以在 unmount 钩子函数中执行相应的操作。但通常 qiankun 会自动处理这些清理工作,因此大多数情况下你不需要手动执行。
export async function unmount() {
// ...
}
5. 使用 HTTP 通信
对于跨域或复杂的通信需求,子应用之间可以通过 HTTP 请求(如 RESTful API 或 GraphQL)来通信。虽然这不是直接的子应用间通信,但它是实现功能的一种有效方式。
6. 使用 WebSocket
如果子应用之间需要实时通信,WebSocket 是一种非常合适的选择。WebSocket 允许在用户的浏览器和服务器之间建立一个持久的连接,并允许双方进行全双工通信。
使用 WebSocket通信的代码栗子:
1. WebSocket 服务端
首先,你需要一个 WebSocket 服务端来处理客户端的连接和消息转发。这可以使用各种 WebSocket 库(如 Node.js 的 ws 或 socket.io)来实现。
// 假设你使用 Node.js 和 ws 库
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
// 在这里你可以解析消息,并根据需要广播给所有客户端或特定客户端
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
});
2. 子应用中的 WebSocket 客户端
每个子应用都会作为 WebSocket 客户端连接到这个服务端。它们可以通过发送和监听消息来进行通信。
// 子应用A中的 WebSocket 客户端
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = function(event) {
socket.send('Hello from SubApp A!'); // 发送消息到服务端
};
socket.onmessage = function(event) {
console.log('Received data from server: ', event.data);
// 这里可以根据消息内容执行相应的操作
};
// 子应用B中的 WebSocket 客户端(与A类似)
// ...
3. 使用 WebSocket 进行子应用间通信
通常,你需要一个机制来区分不同的子应用,并将消息路由到正确的目标。这可以通过在消息中包含目标子应用的标识符来实现。
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
try {
const data = JSON.parse(message);
if (data.target && data.message) {
// 假设我们有一个机制来查找目标客户端(例如,通过映射客户端ID到ws实例)
const targetClient = findTargetClient(data.target);
if (targetClient) {
targetClient.send(JSON.stringify({ message: data.message }));
}
}
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
});
});
function findTargetClient(targetId) {
// 这里应该有一个映射或逻辑来确定哪个客户端是目标客户端
// 例如,你可以使用一个Map来存储 client.id 和 ws 实例的关联
// ...
// 返回目标ws实例或null
}
子应用发送消息的示例:
socket.send(JSON.stringify({
target: 'subAppB', // 目标子应用的标识符
message: 'Hello from SubApp A!' // 要发送的消息
}));
使用 WebSocket 通信 – 注意事项
- 确保 WebSocket 服务端是安全的,并只接受来自受信任源的连接。
- 消息格式应该清晰且易于解析,以避免解析错误。JSON 是一个常见的选择。
- 考虑使用身份验证和授权机制来确保只有授权的子应用可以发送和接收消息。
- 在生产环境中,WebSocket 服务端应该能够处理高并发连接和消息,并且应该有适当的错误处理和日志记录机制。