我正在寻找一种方法,如何在浏览器中的多个选项卡或窗口之间进行通信(在同一个域上,而不是CORS上)而不留下痕迹。有几种解决方案:
第一个可能是最糟糕的解决方案 - 您需要从当前窗口打开一个窗口,然后您只能在保持窗口打开的情况下进行通信。如果您在任何窗口中重新加载页面,您很可能会丢失通信。
第二种方法,使用 postMessage,可能会启用跨域通信,但它遇到与第一种方法相同的问题。您需要维护一个窗口对象。
第三种方式,使用 cookie,在浏览器中存储一些数据,这可以有效地看起来像向同一域上的所有窗口发送消息,但问题是你永远无法知道所有选项卡是否已经读取“消息”在清理之前。您必须实施某种超时来定期读取 cookie。此外,您还受到最大 cookie 长度的限制,即 4KB。
第四种方案,使用localStorage,似乎克服了cookies的限制,甚至可以使用事件监听。接受的答案中描述了如何使用它。
在 2018 年,公认的答案仍然有效,但现代浏览器有一个更新的解决方案,即使用 BroadcastChannel。有关如何使用 BroadcastChannel 在选项卡之间轻松传输消息的简单示例,请参阅其他答案。
为此,您最好使用 BroadcastChannel。请参阅下面的其他答案。但是,如果您仍然喜欢使用 localstorage 在选项卡之间进行通信,请这样做:
为了在选项卡向其他选项卡发送消息时得到通知,您只需绑定“存储”事件。在所有选项卡中,执行以下操作:
$(window).on('storage', message_receive);
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()})
还要记住,广播消息的当前选项卡实际上并没有收到它,只有同一域上的其他选项卡或窗口。
您可能会问,如果用户在 setItem() 调用之后,在 removeItem() 之前加载不同的网页或关闭他的选项卡,会发生什么情况。好吧,根据我自己的测试,浏览器会暂停卸载,直到整个功能message_broadcast()完成。我测试了在那里放了一些很长的 for() 循环,它仍然等待循环完成后再关闭。如果用户只是在中间杀死选项卡,那么浏览器将没有足够的时间将消息保存到磁盘,因此这种方法在我看来是一种安全的方式来发送消息而不留下任何痕迹。
message_broadcast()