无界微前端源码解析:通信机制

43 阅读2分钟

无界微前端源码解析:通信机制

深入分析 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);
});

小结

无界的通信机制:

特性EventBusprops
方向双向单向(主 → 子)
时机任意时刻启动/激活时
类型事件数据/函数
解耦

核心设计:

  1. 全局事件 Map:所有应用共享,支持跨应用通信
  2. 应用隔离:每个应用有独立的 eventObj
  3. 嵌套支持:通过 inject 传递共享 Map
  4. 链式调用on/on/off/$emit 返回 this

下一篇我们将分析插件系统。


📦 源码版本:wujie v1.0.22

上一篇:路由同步

下一篇:插件系统