选项卡或窗口之间的通信

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

  1. 使用窗口对象
  2. 邮政消息
  3. 饼干
  4. 本地存储

第一个可能是最糟糕的解决方案 - 您需要从当前窗口打开一个窗口,然后只要您保持窗口打开,您就可以进行通信。如果您在任何窗口中重新加载页面,则很可能丢失了通信。

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

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

第四种解决方案,使用localStorage,似乎克服了cookie的局限性,甚至可以使用事件进行侦听。如何使用它在接受的答案中进行了描述。

在2018年,公认的答案仍然有效,但是对于现代浏览器来说,有一个更新的解决方案,即使用BroadcastChannel。有关描述如何使用 BroadcastChannel 在选项卡之间轻松传输消息的简单示例,请参阅其他答案。


答案 1

为此,您最好使用广播频道。请参阅下面的其他答案。但是,如果您仍然喜欢使用本地存储进行选项卡之间的通信,请按以下方式操作:

为了在选项卡向其他选项卡发送消息时收到通知,您只需绑定“存储”事件即可。在所有选项卡中,执行以下操作:

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

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

// 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()})

另请记住,广播消息的当前选项卡实际上不会接收它,而只会接收同一域中的其他选项卡或窗口。

您可能会问,如果用户在 removeItem() 之前加载其他网页或关闭其选项卡,则在 setItem() 调用之后会发生什么情况。好吧,根据我自己的测试,浏览器将卸载置于暂停状态,直到整个功能完成。我测试了在那里放一些很长的for()循环,它仍然在等待循环完成之前关闭。如果用户在两者之间杀死了选项卡,那么浏览器将没有足够的时间将消息保存到磁盘,因此这种方法在我看来就像如何发送消息而没有任何痕迹的安全方法。message_broadcast()


答案 2

有一个专用于此目的的现代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