Skip to content

CrossBridge 技术设计详细方案

🔧 核心技术选型

1. 通信技术栈

  • SharedWorker: 同域独立标签页通信的主要方案
  • PostMessage: iframe 与父页面通信的标准方案
  • BroadcastChannel: 跨域标签页通信的备选方案
  • Proxy: 用于 API 代理和消息拦截
  • Set/Map: 高效的页面和消息管理

2. 存储技术栈

  • Memory Cache: 高频访问的临时数据
  • SessionStorage: 会话级别的消息缓存
  • LocalStorage: 持久化的配置和重要消息
  • IndexedDB: 大量消息数据的结构化存储

📐 详细技术设计

1. 核心通信引擎设计

typescript
// 通信引擎核心接口
interface ICommunicationEngine {
  // 初始化引擎
  initialize(config: EngineConfig): Promise<void>;

  // 发送消息
  send(message: Message): Promise<boolean>;

  // 接收消息处理
  onMessage(handler: MessageHandler): void;

  // 获取连接状态
  getConnectionStatus(): ConnectionStatus;

  // 销毁引擎
  destroy(): void;
}

// 引擎配置
interface EngineConfig {
  pageId: string;
  role: PageRole;
  adapters: AdapterConfig[];
  queue: QueueConfig;
  storage: StorageConfig;
}

// 消息结构
interface Message {
  id: string;
  type: string;
  event: string;
  data: any;
  from: string;
  to?: string | string[];
  timestamp: number;
  ttl?: number;
  priority?: number;
}

2. 适配器系统设计

typescript
// 适配器基类
abstract class BaseAdapter {
  protected config: AdapterConfig;
  protected engine: ICommunicationEngine;

  constructor(config: AdapterConfig, engine: ICommunicationEngine) {
    this.config = config;
    this.engine = engine;
  }

  abstract connect(): Promise<void>;
  abstract disconnect(): void;
  abstract send(message: Message): Promise<boolean>;
  abstract onMessage(handler: MessageHandler): void;
  abstract isSupported(): boolean;
}

// SharedWorker 适配器
class SharedWorkerAdapter extends BaseAdapter {
  private worker: SharedWorker | null = null;
  private port: MessagePort | null = null;

  async connect(): Promise<void> {
    if (!this.isSupported()) {
      throw new Error('SharedWorker not supported');
    }

    this.worker = new SharedWorker('/cross-bridge-worker.js');
    this.port = this.worker.port;

    this.port.onmessage = (event) => {
      this.handleMessage(event.data);
    };

    this.port.start();

    // 注册页面
    await this.registerPage();
  }

  async send(message: Message): Promise<boolean> {
    if (!this.port) return false;

    this.port.postMessage(message);
    return true;
  }

  isSupported(): boolean {
    return typeof SharedWorker !== 'undefined';
  }

  private async registerPage(): Promise<void> {
    const registrationMessage: Message = {
      id: generateId(),
      type: 'system',
      event: 'page-register',
      data: {
        pageId: this.config.pageId,
        role: this.config.role,
        origin: window.location.origin,
      },
      from: this.config.pageId,
      timestamp: Date.now(),
    };

    await this.send(registrationMessage);
  }
}

// PostMessage 适配器
class PostMessageAdapter extends BaseAdapter {
  private targetWindow: Window | null = null;
  private targetOrigin: string = '*';

  async connect(): Promise<void> {
    // 根据角色确定目标窗口
    if (this.config.role === 'child') {
      this.targetWindow = window.parent;
    } else if (this.config.role === 'parent') {
      // 父页面需要管理多个 iframe
      this.setupIframeManagement();
    }

    // 监听消息
    window.addEventListener('message', this.handlePostMessage.bind(this));
  }

  async send(message: Message): Promise<boolean> {
    if (!this.targetWindow) return false;

    this.targetWindow.postMessage(message, this.targetOrigin);
    return true;
  }

  private handlePostMessage(event: MessageEvent): void {
    // 验证消息来源
    if (!this.isValidOrigin(event.origin)) {
      return;
    }

    const message = event.data as Message;
    this.handleMessage(message);
  }

  private setupIframeManagement(): void {
    // 管理多个 iframe 的引用
    const iframes = new Map<string, HTMLIFrameElement>();

    // 监听 iframe 加载
    document.addEventListener('DOMContentLoaded', () => {
      const iframeElements = document.querySelectorAll(
        'iframe[data-bridge-id]'
      );
      iframeElements.forEach((iframe) => {
        const id = iframe.getAttribute('data-bridge-id');
        if (id) {
          iframes.set(id, iframe as HTMLIFrameElement);
        }
      });
    });
  }
}

3. 消息队列系统设计

typescript
// 消息队列管理器
class MessageQueueManager {
  private queues: Map<string, MessageQueue> = new Map()
  private storage: StorageManager
  private config: QueueConfig

  constructor(storage: StorageManager, config: QueueConfig) {
    this.storage = storage
    this.config = config
    this.startCleanupTimer()
  }

  // 入队消息
  async enqueue(message: Message): Promise<void> {
    const queueKey = this.getQueueKey(message)
    let queue = this.queues.get(queueKey)

    if (!queue) {
      queue = new MessageQueue(queueKey, this.config)
      this.queues.set(queueKey, queue)
    }

    await queue.enqueue(message)

    // 持久化存储
    if (this.shouldPersist(message)) {
      await this.storage.store(queueKey, message, this.getStorageConfig(message))
    }
  }

  // 出队消息
  async dequeue(queueKey: string): Promise<Message[]> {
    const queue = this.queues.get(queueKey)
    if (!queue) return []

    const messages = await queue.dequeue()

    // 从持久化存储中获取
    const storedMessages = await this.storage.retrieve(queueKey)

    return [...messages, ...storedMessages]
  }

  // 获取队列键
  private getQueueKey(message: Message): string {
    return `${message.to || 'broadcast'}-${message.type}`
  }

  // 判断是否需要持久化
  private shouldPersist(message: Message): boolean {
    const messageTypeConfig = this.config.messageTypes[message.type]
    return messageTypeConfig?.persistent || false
  }

  // 获取存储配置
  private getStorageConfig(message: Message): StorageConfig {
    const messageTypeConfig = this.config.messageTypes[message.type];
    return {
      type: messageTypeConfig?.storage || 'memory',
      ttl: messageTypeConfig?.ttl
    };
  }
}

4. 存储管理器设计

typescript
// 存储策略接口
interface StorageStrategy {
  get<T>(key: string): Promise<T | null>;
  set<T>(key: string, value: T, ttl?: number): Promise<void>;
  remove(key: string): Promise<void>;
  clear(): Promise<void>;
}

// 存储管理器
class StorageManager {
  private strategies = new Map<StorageType, StorageStrategy>();

  constructor(config: StorageConfig) {
    this.initializeStrategies();
  }

  private initializeStrategies(): void {
    this.strategies.set('memory', new MemoryStorage());
    this.strategies.set('local', new LocalStorageStrategy());
    this.strategies.set('session', new SessionStorageStrategy());
    // IndexedDB for large data
    if (typeof indexedDB !== 'undefined') {
      this.strategies.set('indexeddb', new IndexedDBStorage());
    }
  }

  async store<T>(key: string, value: T, config: StorageItemConfig): Promise<void> {
    const strategy = this.strategies.get(config.type);
    if (!strategy) throw new Error(`Storage type ${config.type} not ready`);
    
    await strategy.set(key, value, config.ttl);
  }
}

5. 完全跨域通信扩展设计

为了支持不同一级域名的通信,设计了两种扩展适配器:

5.1 WebSocket 中继适配器 (WebSocketRelayAdapter)

适用于高性能、实时通讯场景。

  • 原理: 客户端 A -> WebSocket 服务端 -> 客户端 B
  • 协议: 自定义 JSON 协议,不仅包含数据,还包含 auth (认证), heartbeat (心跳), relay (中继指令)。
  • 安全: 基于 Token 的身份验证,防止未授权访问。
typescript
interface RelayMessage {
  type: 'auth' | 'heartbeat' | 'relay';
  from: string; // pageId
  to?: string;  // target pageId
  payload: any;
}

5.2 Iframe 桥接适配器 (IframeBridgeAdapter)

适用于无后端开发资源的场景。

  • 原理:
    1. 页面 A 和 页面 B 都加载同一个跨域 iframe (bridge.html)。
    2. 利用iframe的 postMessage 作为中转枢纽。
    3. bridge.html 维护一个 window 注册表,负责路由消息。

6. 总结

本设计采用插件化架构,核心只负责生命周期和消息分发,具体通信由适配器实现。 通过引入 消息队列多级存储,解决了复杂的异步加载和数据持久化问题。 通过 WebSocket RelayIframe Bridge,实现了对全场景跨域通信的覆盖。

Released under the MIT License.