前面讲到了微前端的应用:(94条消息) 微前端应用(qiankun+umi+antd)_他夏了夏天吖的博客-优快云博客https://blog.youkuaiyun.com/zh0623/article/details/130615234?spm=1001.2014.3001.5501今天具体讲一下父子应用的通信问题
主应用和子应用之间的通信和数据共享时,最常用的是自定义事件、消息总线、共享状态库或者通过事件订阅和发布、状态管理等方式来进行数据传递和同步.
1.自定义事件
主应用代码(React + TypeScript):
// 定义一个自定义事件名称
const customEventName = 'customEvent';
// 在主应用中订阅自定义事件
window.addEventListener(customEventName, (event: CustomEvent) => {
// 处理自定义事件的数据
const eventData = event.detail;
console.log('Received event data in main app:', eventData);
});
// 在合适的时机发布自定义事件
const publishEvent = () => {
const eventData = { message: 'Hello from sub app' };
const event = new CustomEvent(customEventName, { detail: eventData });
window.dispatchEvent(event);
};
子应用代码:
// 定义一个自定义事件名称
const customEventName = 'customEvent';
// 在子应用中订阅自定义事件
window.addEventListener(customEventName, (event) => {
// 处理自定义事件的数据
const eventData = event.detail;
console.log('Received event data in sub app:', eventData);
});
// 在合适的时机发布自定义事件
const publishEvent = () => {
const eventData = { message: 'Hello from main app' };
const event = new CustomEvent(customEventName, { detail: eventData });
window.dispatchEvent(event);
};
在上述示例中,主应用和子应用都定义了一个自定义事件名称,分别为 customEventName
。主应用通过 window.addEventListener
方法监听该事件,并在事件处理函数中获取事件的数据。子应用通过 window.dispatchEvent
方法发布该事件,并传递相应的数据。
2. 消息总线
消息总线是一种更高级的跨应用通信方案,它允许不同的子应用之间通过消息队列来进行通信。在React和Vue中,我们可以使用第三方库(如PubSubJS)来实现消息总线。下面是一个简单的例子:在React中:
import React, { useState, useEffect } from 'react';
import PubSub from 'pubsub-js';
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const token = PubSub.subscribe('increment', (msg, data) => {
setCount(data.count);
});
return () => {
PubSub.unsubscribe(token);
};
}, []);
function handleClick() {
PubSub.publish('increment', { count: count + 1 });
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
在Vue中:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="handleClick">Increment</button>
</div>
</template>
<script>
import PubSub from 'pubsub-js';
export default {
data() {
return {
count: 0,
};
},
mounted() {
PubSub.subscribe('increment', (msg, data) => {
this.count = data.count;
});
},
methods: {
handleClick() {
PubSub.publish('increment', { count: this.count + 1 });
},
},
};
</script>
在上面的例子中,我们在React和Vue中都使用了PubSubJS库来实现消息总线。当用户点击“Increment”按钮时,我们会发布一个名为“increment”的消息,并将当前计数器的值作为消息的详细信息传递给其他子应用。同时,我们还在组件中订阅了“increment”消息,并在收到消息时更新计数器的值。
除了上面的方式,消息总线还有以下几种方式:
1. 本地事件总线:使用事件监听和事件触发在同一个应用内部通信。
2. 跨窗口消息传递:使用 postMessage 在不同的浏览器窗口/标签页之间通信。
3. Web Sockets:使用 WebSocket 协议在客户端和服务器之间建立持久连接,进行双向通信。
4. 本地存储事件:使用本地存储监听 storage 事件在不同的浏览器窗口/标签页之间通信。
5. 第三方消息代理:使用第三方服务如 Pusher、Socket.io 等在客户端之间传递消息。
2.1 本地事件总线
React 项目:
js
// React 项目
import { useState } from 'react';
// 创建事件总线
const eventBus = {
on(event, callback) {
document.addEventListener(event, callback);
},
emit(event, data) {
document.dispatchEvent(new CustomEvent(event, { detail: data }));
}
}
function ReactApp() {
const [msg, setMsg] = useState('');
// 监听事件
useEffect(() => {
eventBus.on('message', (e) => {
setMsg(e.detail);
})
}, []);
return <div>{msg}</div>
}
Vue项目:
js
// Vue 项目
// 创建事件总线
const eventBus = new Vue();
methods: {
sendMessage() {
eventBus.$emit('message', 'Hello from Vue!');
}
},
mounted() {
eventBus.$on('message', (msg) => {
this.msg = msg;
});
}
2.2 跨窗口消息传递postMessage
//React:
js
// 发送消息
window.opener.postMessage('Hello from React!', '*');
// 接收消息
window.addEventListener('message', (e) => {
console.log(e.data); // Hello from Vue!
});
//Vue:
js
// 发送消息
window.opener.postMessage('Hello from Vue!', '*');
// 接收消息
window.addEventListener('message', (e) => {
console.log(e.data); // Hello from React!
});
3. Web Sockets进行双向通信
js
// React 项目
import { useState, useEffect } from 'react';
function ReactApp() {
const [msg, setMsg] = useState('');
// 连接 WebSocket
useEffect(() => {
const socket = new WebSocket('ws://localhost:3000');
socket.onmessage = (e) => {
setMsg(e.data);
}
}, []);
return <div>{msg}</div>
}
// Vue 项目
methods: {
sendMessage() {
this.$socket.send('Hello from Vue!');
}
},
sockets: {
connect() {
this.$socket = new WebSocket('ws://localhost:3000');
}
}
4. 本地存储事件:使用本地存储监听 storage 事件在不同的浏览器窗口/标签页之间通信
//React:
js
// 发送消息
localStorage.setItem('message', 'Hello from React!');
// 接收消息
window.addEventListener('storage', (e) => {
console.log(e.newValue); // Hello from Vue!
});
/Vue:
js
// 发送消息
localStorage.setItem('message', 'Hello from Vue!');
// 接收消息
window.addEventListener('storage', (e) => {
console.log(e.newValue); // Hello from React!
});
5. 第三方消息代理:使用第三方服务如 Pusher、Socket.io 等在客户端之间传递消息
//Socket.io:
//React:
js
// 连接 Socket.io
const socket = io('http://localhost:4000');
// 发送消息
socket.emit('message', 'Hello from React!');
// 接收消息
socket.on('message', (msg) => {
console.log(msg); // Hello from Vue!
});
//Vue:
js
// 连接 Socket.io
const socket = io('http://localhost:4000');
// 发送消息
socket.emit('message', 'Hello from Vue!');
// 接收消息
socket.on('message', (msg) => {
console.log(msg); // Hello from React!
});
//Socket.io 服务器:
//js
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
io.on('connection', (socket) => {
socket.on('message', (msg) => {
socket.broadcast.emit('message', msg);
});
});
http.listen(4000);
3.共享状态库
//在React中:javascript
import React from 'react';
import { createStore } from 'redux';
const initialState = { count: 0 };
function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
}
const store = createStore(reducer);
function App() {
const { count } = store.getState();
function handleClick() {
store.dispatch({ type: 'INCREMENT' });
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App;
//在Vue中:javascript
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="handleClick">Increment</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
computed: mapState(['count']),
methods: mapActions(['increment']),
};
</script>
在微前端中,不同的子应用之间可以通过多种方式进行通信和数据共享,不仅限于引用组件的方式。下面是一些常见的场景和解决方案:
1. 引用组件的方式在主应用中引用不同子应用的组件时,可以使用上面提到的自定义事件、消息总线或共享状态库等方式进行通信和数据共享。这种方式通常适用于子应用以组件的形式嵌入到主应用中的场景。
2. 路由的方式如果不同的子应用是通过路由的方式在主应用中引用的,可以使用URL参数、localStorage或cookie等方式进行通信和数据共享。例如,我们可以在URL参数中传递一些数据,或者使用localStorage或cookie来存储一些共享状态。
3. 独立运行的方式如果不同的子应用是独立运行的,它们之间无法直接进行通信和数据共享。但是,我们可以使用一些第三方工具或技术来实现跨域通信和数据共享,例如iframe、postMessage、WebSocket等。总之,不同的场景和需求需要选择不同的通信和数据共享方案。在实际应用中,我们需要根据具体情况来选择最合适的方案。