返回

从0到1实现一个小型Comlink(仿Comlink)

前端

简介

Comlink是一种强大的JavaScript API,允许在不同的窗口、工作线程或浏览器标签之间安全高效地进行跨域通信。它利用可信代理的概念,为我们提供了一种简便的方法来跨越不同源之间传输和调用函数。

构建Comlink

为了从头开始构建一个小型Comlink,我们需要创建一个称为代理 的对象,该对象将作为我们跨域通信的桥梁。该代理应具有以下方法:

  • postMessage(): 将消息发送到另一端。
  • onMessage(callback): 注册一个回调函数来处理传入的消息。

步骤 1:创建代理

class Proxy {
  constructor() {
    this.callbacks = [];
    this.onmessage = null;
  }

  postMessage(message) {
    if (this.onmessage) this.onmessage(message);
    this.callbacks.forEach(cb => cb(message));
  }

  addEventListener(type, callback) {
    if (type === 'message') this.callbacks.push(callback);
  }
}

步骤 2:绑定代理

要将代理与其他窗口连接起来,我们需要将其绑定到一个函数。这将创建一个可以安全地在不同窗口之间传输的代理副本。

const proxy = new Proxy();
const boundProxy = proxy.bind(window);

步骤 3:在另一个窗口中创建接收器

在另一个窗口中,我们需要创建一个接收器来接收来自代理的消息。

const receiver = new Proxy();
receiver.onmessage = (message) => {
  // 处理消息
};
receiver.addEventListener('message', boundProxy);

Q1:为什么在代理中返回一个fn,需要bind?

代理中返回一个函数是因为代理自身是一个可信代理,而可信代理可以在不同的窗口或线程之间安全地传递。bind()用于将代理绑定到一个特定对象,以便可以在其他窗口中使用该代理。

Q2:为什么等待一个代理会产生对then的一次拦截?

等待一个代理会产生对then的一次拦截,因为代理实际上是一个可信代理,可以作为promise来使用。then()拦截允许我们注册一个回调函数,当代理收到消息时该函数将被调用。

示例用法

我们的小型Comlink现在可以用于在不同的窗口之间传输和调用函数。例如,我们可以发送一个函数调用到另一个窗口,该函数将在接收窗口中执行。

// 在第一个窗口中
boundProxy.postMessage({
  type: 'call',
  functionName: 'greet',
  args: ['Alice']
});

// 在第二个窗口中
receiver.onmessage = (message) => {
  if (message.type === 'call') {
    const result = window[message.functionName](...message.args);
    boundProxy.postMessage({
      type: 'return',
      result
    });
  }
};

总结

通过创建一个小型的Comlink实现,我们深入了解了跨域通信的机制。Comlink在分布式系统和构建可扩展、弹性网络应用程序方面有着广泛的应用。从头开始构建Comlink有助于我们深入理解其底层原理,并能够定制和扩展其功能以满足特定的需求。