无界微前端源码解析:通信机制
深入分析 EventBus 事件总线和 props 数据传递的实现原理。
通信方式
无界提供两种通信方式:
| 方式 | 场景 | 特点 |
|---|---|---|
| EventBus | 事件驱动 | 发布订阅,解耦 |
| props | 数据传递 | 单向数据流 |
EventBus 实现
// packages/wujie-core/src/event.ts
export type EventObj = { [event: string]: Array<Function> };
// 全局事件存储
export const appEventObjMap = (() => {
if (window.__WUJIE_INJECT?.appEventObjMap) {
return window.__WUJIE_INJECT.appEventObjMap;
} else {
const cacheMap = window.__POWERED_BY_WUJIE__
? window.__WUJIE.inject.appEventObjMap
: new Map<String, EventObj>();
window.__WUJIE_INJECT = { ...window.__WUJIE_INJECT, appEventObjMap: cacheMap };
return cacheMap;
}
})();
export class EventBus {
private id: string;
private eventObj: EventObj;
constructor(id: string) {
this.id = id;
this.$clear();
// 初始化事件对象
if (!appEventObjMap.get(this.id)) {
appEventObjMap.set(this.id, {});
}
this.eventObj = appEventObjMap.get(this.id);
}
}
事件监听
// packages/wujie-core/src/event.ts
export class EventBus {
// 监听事件
public $on(event: string, fn: Function): EventBus {
const cbs = this.eventObj[event];
if (!cbs) {
this.eventObj[event] = [fn];
return this;
}
// 避免重复注册
if (!cbs.includes(fn)) cbs.push(fn);
return this;
}
// 监听所有事件
public $onAll(fn: (event: string, ...args: Array<any>) => any): EventBus {
return this.$on(WUJIE_ALL_EVENT, fn);
}
// 一次性监听
public $once(event: string, fn: Function): void {
const on = function (...args: Array<any>) {
this.$off(event, on);
fn(...args);
}.bind(this);
this.$on(event, on);
}
}
取消监听
// packages/wujie-core/src/event.ts
export class EventBus {
// 取消监听
public $off(event: string, fn: Function): EventBus {
const cbs = this.eventObj[event];
if (!event || !cbs || !cbs.length) {
warn(`${event} ${WUJIE_TIPS_NO_SUBJECT}`);
return this;
}
let cb;
let i = cbs.length;
while (i--) {
cb = cbs[i];
if (cb === fn) {
cbs.splice(i, 1);
break;
}
}
return this;
}
// 取消监听所有事件
public $offAll(fn: Function): EventBus {
return this.$off(WUJIE_ALL_EVENT, fn);
}
}
事件发送
// packages/wujie-core/src/event.ts
export class EventBus {
// 发送事件
public $emit(event: string, ...args: Array<any>): EventBus {
let cbs = [];
let allCbs = [];
// 遍历所有应用的事件对象
appEventObjMap.forEach((eventObj) => {
if (eventObj[event]) cbs = cbs.concat(eventObj[event]);
if (eventObj[WUJIE_ALL_EVENT]) allCbs = allCbs.concat(eventObj[WUJIE_ALL_EVENT]);
});
if (!event || (cbs.length === 0 && allCbs.length === 0)) {
warn(`${event} ${WUJIE_TIPS_NO_SUBJECT}`);
} else {
try {
// 执行特定事件回调
for (let i = 0, l = cbs.length; i < l; i++) cbs[i](...args);
// 执行全局事件回调
for (let i = 0, l = allCbs.length; i < l; i++) allCbs[i](event, ...args);
} catch (e) {
error(e);
}
}
return this;
}
}
清空事件
// packages/wujie-core/src/event.ts
export class EventBus {
// 清空当前应用的所有监听事件
public $clear(): EventBus {
const eventObj = appEventObjMap.get(this.id) ?? {};
const events = Object.keys(eventObj);
events.forEach((event) => delete eventObj[event]);
return this;
}
}
全局 bus
// packages/wujie-core/src/index.ts
export const bus = new EventBus(Date.now().toString());
主应用使用:
import { bus } from 'wujie';
// 监听事件
bus.$on('user-login', (user) => {
console.log('用户登录:', user);
});
// 发送事件
bus.$emit('app-ready', { version: '1.0.0' });
子应用 bus
// packages/wujie-core/src/sandbox.ts
constructor(options) {
// 创建子应用专属 bus
this.bus = new EventBus(this.id);
// 提供给子应用
this.provide = { bus: this.bus };
}
子应用使用:
// 子应用中
const { bus } = window.$wujie;
// 监听事件
bus.$on('app-ready', (data) => {
console.log('主应用就绪:', data);
});
// 发送事件
bus.$emit('user-login', { name: 'John' });
props 传递
// 主应用
startApp({
name: 'vue3',
url: 'http://localhost:7300/',
el: '#container',
props: {
user: { name: 'John', age: 25 },
api: {
getUserInfo: () => fetch('/api/user'),
logout: () => { /* ... */ },
},
},
});
子应用获取:
// 子应用中
const { props } = window.$wujie;
console.log(props.user); // { name: 'John', age: 25 }
props.api.getUserInfo().then(/* ... */);
props 更新
// packages/wujie-core/src/sandbox.ts
public async active(options): Promise<void> {
const { props } = options;
// 更新 props
this.provide.props = props ?? this.provide.props;
}
重新激活时可以传递新的 props:
// 更新 props
startApp({
name: 'vue3',
url: 'http://localhost:7300/',
el: '#container',
props: {
user: { name: 'Jane', age: 30 }, // 新数据
},
});
$wujie 接口
// packages/wujie-core/src/iframe.ts
function patchIframeVariable(iframeWindow: Window, wujie: WuJie, appHostPath: string): void {
// 子应用接口
iframeWindow.$wujie = wujie.provide;
}
// provide 结构
this.provide = {
bus: this.bus, // 事件总线
shadowRoot: undefined, // Shadow DOM(激活后设置)
props: undefined, // 传递的数据
location: this.proxyLocation, // 代理的 location
};
嵌套场景
子应用嵌套时,事件可以跨层传递:
// packages/wujie-core/src/sandbox.ts
constructor(options) {
// 传递 inject 给嵌套子应用
if (window.__POWERED_BY_WUJIE__) {
this.inject = window.__WUJIE.inject;
} else {
this.inject = {
idToSandboxMap: idToSandboxCacheMap,
appEventObjMap, // 共享事件 Map
mainHostPath: window.location.protocol + "//" + window.location.host,
};
}
}
事件存储共享:
// packages/wujie-core/src/event.ts
export const appEventObjMap = (() => {
// 嵌套场景:使用父应用的 appEventObjMap
if (window.__POWERED_BY_WUJIE__) {
return window.__WUJIE.inject.appEventObjMap;
}
// 主应用:创建新的 Map
return new Map<String, EventObj>();
})();
使用示例
主应用 → 子应用
// 主应用
import { bus } from 'wujie';
// 发送事件
bus.$emit('theme-change', { theme: 'dark' });
// 子应用
window.$wujie.bus.$on('theme-change', ({ theme }) => {
document.body.className = theme;
});
子应用 → 主应用
// 子应用
window.$wujie.bus.$emit('user-logout');
// 主应用
import { bus } from 'wujie';
bus.$on('user-logout', () => {
router.push('/login');
});
子应用 → 子应用
// 子应用 A
window.$wujie.bus.$emit('cart-update', { count: 5 });
// 子应用 B
window.$wujie.bus.$on('cart-update', ({ count }) => {
updateCartBadge(count);
});
监听所有事件
// 主应用
import { bus } from 'wujie';
bus.$onAll((event, ...args) => {
console.log(`[Event] ${event}:`, args);
});
小结
无界的通信机制:
| 特性 | EventBus | props |
|---|---|---|
| 方向 | 双向 | 单向(主 → 子) |
| 时机 | 任意时刻 | 启动/激活时 |
| 类型 | 事件 | 数据/函数 |
| 解耦 | 高 | 低 |
核心设计:
- 全局事件 Map:所有应用共享,支持跨应用通信
- 应用隔离:每个应用有独立的 eventObj
- 嵌套支持:通过 inject 传递共享 Map
- 链式调用:off/$emit 返回 this
下一篇我们将分析插件系统。
📦 源码版本:wujie v1.0.22
上一篇:路由同步
下一篇:插件系统