在浏览器选项卡和Windows之间共享Websocket连接
#javascript #网络开发人员 #typescript #dotnet

WebSocket连接就像特殊的通信渠道,允许Web浏览器和服务器实时交流。但是,当您在浏览器中打开多个选项卡或窗口时,每个选项卡都会建立自己的Websocket连接。这可能会浪费并影响您的Web应用程序的性能。

但不用担心!在此博客文章中,我们将探索一个解决方案,当用户在不同的选项卡或Windows 中打开应用程序时,您可以优化WebSocket的用法。通过在这些选项卡和Windows之间共享Websocket连接,我们可以使您的Web应用程序更有效,更平稳地工作。因此,让我们潜入并发现一些有用的策略,以实现浏览器选项卡和Windows之间的无缝Websocket连接共享。

介绍共享工人

为了实现浏览器选项卡和Windows之间的WebSocket连接共享,我们可以利用网络工人的力量。 Web工作人员本质上是单独的线程,完全独立于其他脚本,无法访问DOM 。他们可以在后台执行任务,这使得它们对于执行复杂计算并防止网页冻结或变得无响应特别有用。

共享工人是一种特殊类型的Web工作人员,可以在多个浏览器选项卡或窗口之间共享 s s。与专门用于单个网页的普通网络工作者不同,共享工人独立于任何特定的网页或用户界面。它们允许多个浏览器上下文(例如选项卡或Windows)进行交流和共享资源。通过利用共享工人,我们可以创建一个持久的实体,该实体可以管理Websocket连接并促进其在不同的浏览器上下文中共享。另外,我们不必担心关闭联系。如果没有标签或Windows引用它们,共享工人将停止存在,因此连接将自动关闭。

与共享工人实施WebSocket连接共享

要在浏览器选项卡和Windows之间实现WebSocket连接共享,我们将按照以下步骤进行操作:

创建共享工人

首先使用共享工人API创建共享工人。该工人将充当负责管理WebSocke连接的中心实体。添加一个消息处理程序以接收来自工人的数据,并随心所欲。

// No bundler
const worker = new SharedWorker("./worker.js");

// Webpack 5
const worker = new SharedWorker(new URL("./worker.ts", import.meta.url));

worker.port.addEventListener('message', (event: MessageEvent): void => {
  // Do whatever you like with data
});
worker.port.start();

建立Websocket连接

在共享工作人员中,创建一个WebSocket对象并与服务器建立连接。此连接将在所有浏览器选项卡和Windows之间共享。添加连接处理程序并将端口存储在数组中。将每个接收到的消息转发到所有MessagePort对象也至关重要。

const ports: MessagePort[] = [];
const ws = new WebSocket('wss://some-url.com');

addEventListener('connect', (event: MessageEvent): void => {
  const port = event.ports[0];
  this.ports.push(port);
});

ws.addEventListener('message', (event: MessageEvent): void => {
  ports.forEach((port) => {
    port.postMessage(event.data);
  });
});

就是这样。现在,您将能够在所有选项卡和Windows中共享连接并同时接收消息。

挑战和解决方案:内存管理

虽然共享工人提供有效的解决方案,但有一个挑战要解决:内存管理。由于没有内置机制来确定消息端口是否仍处于活动状态,因此参考可能会随着时间的推移而积累,可能导致内存泄漏。为了减轻此问题,我们可以利用弱者。 通过使用feekRefs,我们可以检查是否已收集了消息端口,以确保有效的资源利用。

export class BrowserPort {
  private readonly weakRef: WeakRef<MessagePort>;

  constructor(port: MessagePort) {
    this.weakRef = new WeakRef(port);
    port.start();
  }

  isAlive(): boolean {
    return !!this.weakRef.deref();
  }

  postMessage(message: unknown): void {
    this.weakRef.deref()?.postMessage(message);
  }

  addEventListener(event: string, handler: (event: Event) => void): void {
    this.weakRef.deref()?.addEventListener(event, handler);
  }

  removeEventListener(event: string, handler: (event: Event) => void): void {
    this.weakRef.deref()?.removeEventListener(event, handler);
  }

  close(): void {
    this.weakRef.deref()?.close();
  }
}

let ports: BrowserPort[] = [];

addEventListener('connect', (event: MessageEvent): void => {
  const port = event.ports[0];
  ports.push(new BrowserPort(port));
});

// At some point we would like to send a message

if (port.isAlive()) {
  // It's alive! We can send a message.
  port.sendMessage('msg');
} else {
  // It's dead! We have to remove it from the array.
  ports = ports.filter(innerPort => innerPort !== port); 
}

另一个解决方案是在onbeforeunload事件处理程序的主体中发送一条特殊的控制消息。请记住,此方法不可靠,浏览器可能会选择忽略该消息而不是将消息传递给工人。

// Browser
window.addEventListener('onbeforeunload', (): void => {
  worker.port.sendMessage('UNLOAD');
});

// Worker
let ports: MessagePort[] = [];

addEventListener('connect', (event: MessageEvent): void => {
  const port = event.ports[0];
  ports.push(port);

  port.addEventListener('message', (event: MessageEvent): void => {
    if (event.data === 'UNLOAD') {
      ports = ports.filter(innerPort => innerPort !== port); 
    }
  });
});

结论

在此博客文章中,我们探索了浏览器选项卡和Windows之间共享WebSocket连接的概念。通过利用共享工人,我们可以优化资源使用情况并增强在多标签或多窗口方案中的Web应用程序的性能。

共享工人提供了一种有力的机制,可以建立一个中央实体,该中心实体管理Websocket连接并促进不同浏览器上下文之间的通信。 通过创建共享工作者并利用MessagePort API,我们可以在浏览器选项卡和Windows之间无缝传递消息,从而使它们能够共享一个Websocket Connection

虽然需要考虑挑战,例如内存管理,但使用诸如弱者之类的适当技术可以帮助减轻潜在问题。实施合适的策略以确保有效的资源利用并防止内存泄漏很重要。

通过实现WebSocket连接共享,您可以提高Web应用程序的效率,响应能力和整体用户体验。用户可以通过多个选项卡或窗口无缝与您的应用程序进行互动,同时避免不必要的资源重复。

快乐编码!


Szymon Chmal,高级前端开发人员 @ Bright Invictions