qiankun 作为流行的微前端解决方案,提供了多种子应用间通信的方式。以下是全面的通信方法总结:
1. 基于 props 的通信(主应用与子应用)
主应用向子应用传递数据:
javascript
// 主应用注册子应用时传递数据
registerMicroApps([
{
name: 'subApp',
entry: '//localhost:7100',
container: '#subapp-container',
activeRule: '/sub-app',
props: { // 通过props传递数据
basePath: '/sub-app',
globalState: mainAppState,
onStateChange: (state) => { /* 回调函数 */ }
}
}
]);
子应用接收数据:
javascript
// 子应用入口文件
export async function mount(props) {
console.log(props.basePath); // 使用主应用传递的数据
props.onGlobalStateChange((state, prevState) => {
// 监听全局状态变化
});
}
2. 全局状态管理(推荐方式)
使用 qiankun 的 initGlobalState
主应用初始化全局状态:
javascript
// 主应用
import { initGlobalState } from 'qiankun';
const initialState = { user: { name: 'Admin' } };
const actions = initGlobalState(initialState);
// 监听状态变化
actions.onGlobalStateChange((state, prev) => {
console.log('主应用观察到的状态变化:', state, prev);
});
// 设置状态
actions.setGlobalState({ ...initialState, menu: ['home'] });
子应用使用全局状态:
javascript
// 子应用
export function mount(props) {
// 监听全局状态
props.onGlobalStateChange((state, prev) => {
console.log('子应用收到的状态:', state);
}, true); // 第二个参数为是否立即触发
// 更新全局状态
props.setGlobalState({ user: { name: 'SubAppUser' } });
}
3. 自定义事件通信(EventBus 模式)
创建全局事件总线:
javascript
// 主应用或公共文件
class EventBus {
constructor() {
this.events = {};
}
$on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
$emit(event, ...args) {
(this.events[event] || []).forEach(cb => cb(...args));
}
}
// 挂载到全局
window.microFrontendEventBus = new EventBus();
子应用间通信:
javascript
// 应用A发送事件
window.microFrontendEventBus.$emit('data-shared', { key: 'value' });
// 应用B监听事件
window.microFrontendEventBus.$on('data-shared', (data) => {
console.log('收到数据:', data);
});
4. 使用 Redux/Vuex 等状态管理库
共享 store 方案:
javascript
// 主应用创建store并传递给子应用
const store = createStore(reducer, initialState);
registerMicroApps([{
name: 'subApp',
entry: '//localhost:7100',
props: { store }
}]);
// 子应用使用
export function mount({ store }) {
store.subscribe(() => {
console.log('状态变化:', store.getState());
});
store.dispatch({ type: 'UPDATE', payload: {} });
}
5. localStorage/sessionStorage 通信
javascript
// 应用A设置数据
localStorage.setItem('shared-data', JSON.stringify({ key: 'value' }));
// 应用B监听变化
window.addEventListener('storage', (event) => {
if (event.key === 'shared-data') {
console.log('数据已更新:', JSON.parse(event.newValue));
}
});
6. 使用 window 全局变量
javascript
// 主应用设置全局对象
window.__MAIN_APP_STATE__ = {
token: 'abc123',
updateToken: (newToken) => { /* ... */ }
};
// 子应用访问
const token = window.__MAIN_APP_STATE__.token;
window.__MAIN_APP_STATE__.updateToken('newToken');
7. 使用 BroadcastChannel API(跨标签页通信)
javascript
// 所有应用中使用相同的channel名称
const channel = new BroadcastChannel('micro-frontend-channel');
// 发送消息
channel.postMessage({ type: 'DATA_UPDATE', payload: {} });
// 接收消息
channel.onmessage = (event) => {
console.log('收到消息:', event.data);
};
8. 使用 postMessage(iframe 式通信)
主应用与子应用通信:
javascript
// 主应用发送消息
window.postMessage({ type: 'FROM_MAIN', data: {} }, '*');
// 子应用接收
window.addEventListener('message', (event) => {
if (event.data.type === 'FROM_MAIN') {
console.log(event.data.data);
}
});
最佳实践建议
- 简单场景 :使用 qiankun 内置的
initGlobalState
即可满足需求 - 复杂场景:推荐组合使用全局状态管理 + 自定义事件
- 安全考虑 :
- 避免直接暴露修改全局状态的方法
- 对通信数据进行校验
- 使用命名空间避免冲突(如
__MAIN_APP__
前缀)
- 性能优化 :
- 避免高频通信
- 对大体积数据考虑使用共享存储
- 类型安全(TypeScript 项目):
typescript
// 定义全局状态类型
interface GlobalState {
user: {
name: string;
role: string;
};
token?: string;
}
// 安全访问全局状态
if (window.__POWERED_BY_QIANKUN__) {
const state = (props as { getGlobalState: () => GlobalState }).getGlobalState();
}
通信方式对比
方法 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
props 传递 | 主-子单向通信 | 简单直接 | 只能主传子,不能子传子 |
initGlobalState | 任意应用间通信 | 官方推荐,内置支持 | 需要手动管理状态变更 |
自定义事件 | 任意应用间通信 | 灵活,解耦 | 需要自己实现事件管理 |
全局状态管理 | 复杂状态共享 | 专业状态管理 | 增加复杂度 |
localStorage | 持久化数据共享 | 简单,跨标签页 | 数据类型受限,安全性问题 |
window 全局变量 | 简单数据共享 | 非常直接 | 容易造成污染,不安全 |
BroadcastChannel | 跨标签页通信 | 原生支持,高效 | 兼容性考虑 |
postMessage | iframe 式隔离环境 | 安全隔离 | 使用较复杂 |
选择通信方式时,应根据项目复杂度、团队技术栈和安全要求综合评估。对于大多数 qiankun 项目,组合使用 initGlobalState
和自定义事件通常是最佳选择。