选项卡或窗口之间的通信

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

  1. 使用窗口对象
  2. < a href = " https://stackoverflow.com/questions/2236828/javascript-communication-between-tabs-windows-with-same-origin/12514384 " > postMessage < / >
  3. 4079423 < a href = " https://stackoverflow.com/questions/4079280/javascript-communication-between-browser-tabs-windows/4079423 " >饼干< / >
  4. 本地文件< a href = " https://stackoverflow.com/questions/1100336/sending-a-message-to-all-open-windows-tabs-using-javascript " > < / >

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

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

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

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

156365 次浏览

为此,您最好使用BroadcastChannel。请看下面的其他答案。然而,如果你仍然喜欢使用localstorage在选项卡之间进行通信,可以这样做:

为了在一个选项卡向其他选项卡发送消息时得到通知,你只需要绑定一个'storage'事件。在所有选项卡中,这样做:

$(window).on('storage', message_receive);

每次在任何其他选项卡中设置localStorage的任何值时,函数message_receive将被调用。事件监听器还包含新设置为localStorage的数据,因此您甚至不需要解析localStorage对象本身。这非常方便,因为您可以在设置后立即重置值,以有效地清除任何痕迹。下面是消息传递的函数:

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
localStorage.setItem('message',JSON.stringify(message));
localStorage.removeItem('message');
}




// receive message
//
function message_receive(ev)
{
if (ev.originalEvent.key!='message') return; // ignore other keys
var message=JSON.parse(ev.originalEvent.newValue);
if (!message) return; // ignore empty msg or msg reset


// here you act on messages.
// you can send objects like { 'command': 'doit', 'data': 'abcd' }
if (message.command == 'doit') alert(message.data);


// etc.
}

所以现在一旦你的选项卡绑定了onstorage事件,你实现了这两个函数,你可以简单地广播一个消息给其他选项卡调用,例如:

message_broadcast({'command':'reset'})

请记住,两次发送完全相同的消息将只传播一次,因此如果需要重复消息,请为它们添加一些唯一标识符,例如

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

还要记住,广播消息的当前选项卡实际上并没有接收到消息,只接收到同一域中的其他选项卡或窗口。

你可能会问,如果用户加载了一个不同的网页,或者在调用setItem()之后在removeItem()之前关闭了他的标签,会发生什么。嗯,从我自己的测试来看,浏览器会暂停卸载,直到整个函数message_broadcast()完成。我测试了一些很长的for()循环在那里,它仍然等待循环完成之前关闭。如果用户在中间终止了标签,那么浏览器将没有足够的时间将消息保存到磁盘,因此这种方法在我看来是一种安全的发送消息的方式,没有任何痕迹。

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

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

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

它使用postMessagesessionStorage api的组合,使通信更容易和可靠。


有不同的方法,每一个都有自己的优点和缺点。让我们分别讨论一下:

  1. < p > LocalStorage

    优点:

    1. Web存储可以简单地看作是对cookie的改进,提供了更大的存储容量。如果你查看Mozilla源代码,我们可以看到5120 KB(在Chrome上等于250万个字符5 MB)是整个域的默认存储大小。这为您提供了比典型的4 KB cookie大得多的工作空间。
    2. 数据不会为每个HTTP请求(HTML、图像、JavaScript、CSS等)发送回服务器,从而减少了客户端和服务器之间的流量。
    3. 存储在localStorage中的数据将一直存在,直到显式删除为止。所做的更改将被保存,并可用于当前和将来对站点的所有访问。

    缺点:

    1. 它在同源策略上工作。因此,存储的数据只能在相同的原点上可用。
  2. < p > Cookies

    优点:

    1. 和别人相比,我没什么了不起的。

    缺点:

    1. 4kb的限制是针对整个cookie的,包括名称、值、有效期等。为了支持大多数浏览器,请将名称保持在4000字节以下,并且cookie的总体大小保持在4093字节以下。
    2. 对于每个HTTP请求(HTML、图像、JavaScript、CSS等),数据都会被发送回服务器,这增加了客户端和服务器之间的流量。

    通常,允许执行以下操作:

    • 总共300个cookie
    • 4096个字节每个cookie
    • 20个饼干每个域
    • 81920个字节每个域(给定20个最大大小4096 = 81920字节的cookie。)
  3. < p > sessionStorage

    优点:

    1. 它类似于localStorage
    2. 更改仅适用于每个窗口(或Chrome和Firefox等浏览器中的选项卡)。所做的更改将被保存,并可用于当前页面,以及在同一窗口上对该网站的未来访问。一旦窗口关闭,存储就会被删除

    缺点:

    1. 数据仅在设置数据的窗口/选项卡内可用。
    2. 数据不是持久的,也就是说,一旦窗口/选项卡关闭,它就会丢失。
    3. localStorage一样,tt在同源策略上工作。因此,存储的数据只能在相同的原点上可用。
  4. < p > PostMessage

    优点:

    1. 安全地启用跨源通信。
    2. 作为一个数据点,WebKit实现(由Safari和Chrome使用)目前不强制任何限制(除了内存不足造成的限制)。

    缺点:

    1. 需要从当前窗口打开一个窗口,然后可以通信,只要你保持窗口打开。
    2. < >强安全问题< / >强 -通过postMessage发送字符串是你将拾取其他JavaScript插件发布的其他postMessage事件,所以一定要实现一个targetOrigin,并对传递给messages侦听器的数据进行完整性检查。
  5. < p > < A href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage" rel="nofollow noreferrer">PostMessage + < A href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage" rel="nofollow noreferrer">SessionStorage

    使用postMessage在多个选项卡之间通信,同时在所有新打开的选项卡/窗口中使用sessionStorage持久化传递的数据。只要选项卡/窗口保持打开状态,数据就会被持久化。因此,即使打开选项卡/窗口被关闭,打开的选项卡/窗口即使在刷新后也会有完整的数据。

我为此写了一个JavaScript库,名为< >强AcrossTabs < / >强,它使用postMessage API在跨源选项卡/窗口和sessionStorage之间进行通信,以持久化打开的选项卡/窗口标识,只要它们存在。

有一个专门用于此目的的现代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连接上失败。

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

您可以使用它在同一浏览器和域中的选项卡/窗口之间进行通信。该库使用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文件,并从源域中调用proxy函数:

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

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

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

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

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

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

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

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

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