我正在寻找一种方法,如何在浏览器中的多个选项卡或窗口(在同一域上,而不是CORS)之间进行通信而不留下痕迹。有几种解决方案:

使用window对象 postMessage 饼干 localStorage

第一种可能是最糟糕的解决方案——您需要从当前窗口打开一个窗口,然后您只能在保持窗口打开的情况下进行通信。如果在任何窗口中重新加载页面,很可能会失去通信。

第二种方法(使用postMessage)可能支持跨源通信,但它遇到了与第一种方法相同的问题。您需要维护一个窗口对象。

第三种方法是使用cookie,在浏览器中存储一些数据,这实际上看起来像是向同一域上的所有窗口发送消息,但问题是,在清理之前,您永远无法知道所有选项卡是否已经阅读了“消息”。您必须实现某种类型的超时来定期读取cookie。此外,您还受到最大cookie长度(4 KB)的限制。

第四种解决方案,使用localStorage,似乎克服了cookie的限制,甚至可以使用事件监听。如何使用它在公认的答案中有描述。


当前回答

这是一个开发存储部分的Tomas M的答案Chrome。我们必须添加一个监听器:

window.addEventListener("storage", (e)=> { console.log(e) } );

在存储器中加载/保存项目不会触发此事件-我们必须手动通过

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

现在,所有打开的选项卡都将接收该事件。

其他回答

这是一个开发存储部分的Tomas M的答案Chrome。我们必须添加一个监听器:

window.addEventListener("storage", (e)=> { console.log(e) } );

在存储器中加载/保存项目不会触发此事件-我们必须手动通过

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

现在,所有打开的选项卡都将接收该事件。

我在我的博客上写过一篇文章:跨浏览器选项卡共享sessionStorage数据。

使用一个库,我创建了storageManager。你可以通过以下方法实现:

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

还有其他方便的方法来处理其他场景。

有一个基于localStorage的小开源组件,可以在相同来源的选项卡/窗口之间同步和通信(免责声明-我是贡献者之一!)

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

注:我冒昧地在这里推荐它,因为当事件几乎同时发生时,大多数“锁/互斥/同步”组件在websocket连接上失败。

我创建了一个syend .js库,用于在浏览器选项卡和窗口之间发送消息。该库没有任何外部依赖项。

您可以使用它在同一浏览器和域中的选项卡/窗口之间进行通信。该库使用BroadcastChannel(如果支持)或localStorage中的存储事件。

API非常简单:

sysend.on('foo', function(data) {
    console.log(data);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo', ["hello", "world"]);
sysend.broadcast('foo'); // empty notification

当您的浏览器支持BroadcastChannel时,它会发送一个文本对象(但实际上是由浏览器自动序列化的),如果不支持,则先将其序列化为JSON,然后在另一端反序列化。

最新版本还提供了一个帮助API,用于创建跨域通信的代理(它需要目标域上的单个HTML文件)。

这里是一个演示。

新版本也支持跨域通信,如果你在目标域中包含一个特殊的proxy.html文件,并从源域中调用代理函数:

sysend.proxy('https://target.com');

(proxy.html是一个非常简单的HTML文件,它只有一个带有库的脚本标记)。

如果你想要双向通信,你需要在其他域上做同样的事情。

注意:如果您将使用localStorage实现相同的功能,则Internet Explorer中存在一个问题。存储事件被发送到相同的窗口,这触发了事件,对于其他浏览器,它只被其他选项卡/窗口调用。

人们应该考虑使用的另一种方法是共享工作者。我知道这是一个前沿的概念,但你可以在共享工作上创建一个中继,它比本地存储快得多,并且不需要父/子窗口之间的关系,只要你在同一个原点上。

在这里可以看到我关于这个问题的一些讨论。