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

使用window对象 postMessage 饼干 localStorage

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

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

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

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


当前回答

对于那些寻找不基于jQuery的解决方案的人来说,这是一个由Thomas M提供的纯JavaScript版本的解决方案:

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}

其他回答

我在我的博客上写过一篇文章:跨浏览器选项卡共享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

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

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

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

我创建了一个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中存在一个问题。存储事件被发送到相同的窗口,这触发了事件,对于其他浏览器,它只被其他选项卡/窗口调用。

有一个专门用于此目的的现代API -广播频道

简单如下:

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

消息没有必要只是一个DOMString。任何类型的对象都可以被发送。

也许,除了API的干净性之外,这个API的主要好处是没有对象字符串化。

它目前只在Chrome和Firefox中被支持,但是你可以找到一个使用localStorage的polyfill。

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

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

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

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

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