标签或窗口之间的通信

我正在寻找如何在浏览器(在同一个域,而不是CORS)的多个标签或窗口之间进行通信,而不留下痕迹。 有几个解决scheme:

  1. 使用窗口对象
  2. 的postMessage
  3. cookies
  4. localStorage的

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

第二种方法,使用postMessage,可能使跨源通信,但遭受与第一种方法相同的问题。 你需要维护一个窗口对象。

第三种方法,使用cookie,在浏览器中存储一些数据,这可以看起来像是发送一个消息到同一个域的所有窗口,但问题是,你永远不知道所有的标签页是否已经阅读过“消息”打扫干净。 您必须实施某种超时才能定期读取cookie。 此外,您受限于cookie的最大长度,即4KB。

第四个解决scheme,使用localStorage,似乎克服了cookies的限制,甚至可以监听使用事件。 怎么样?

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

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

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

 // 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()之后加载不同的网页或closures其选项卡,会发生什么情况。 那么,从我自己的testing中,浏览器就会放弃卸载,直到整个函数message_broadcast()完成。 我testing了在()周期内放置一些很长的时间,并且在closures之前仍然等待周期结束。 如果用户只是在中间杀死标签,那么浏览器将没有足够的时间将消息保存到磁盘,因此,这种方法在我看来似乎是安全的,不需要任何痕迹就可以发送消息。 评论欢迎。

对于那些寻找不是基于jQuery的解决scheme的人来说,这是Thomas M提供的解决scheme的普通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); } } 

有一个专门用于此目的的现代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的主要好处 – 没有对象的string化。

目前仅支持 Chrome和Firefox,但您可以find使用localStorage的polyfill。

Checkout AcrossTabs跨源浏览器标签之间的轻松沟通。 它使用postMessage和sessionStorage API的组合来使通信变得更加容易和可靠。


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

  1. localStorage的

    优点

    1. Web存储可以被简单地看作是对cookie的改进,提供更大的存储容量。 如果您查看Mozilla源代码,我们可以看到5120KB5MB ,相当于Chrome上的250万个字符 )是整个域的默认存储大小。 与典型的4KB Cookie相比,这样可以为您提供更多的空间。
    2. 数据不会被发送回服务器,每个HTTP请求(HTML,图像,JavaScript,CSS等) – 减less客户端和服务器之间的通信量。
    3. 存储在localStorage中的数据一直存在,直到被明确删除。 所做的更改已保存,并可用于当前和未来对该网站的所有访问。

    缺点

    1. 它在同源策略上工作 。 所以,存储的数据将只能在同一个来源。
  2. cookies

    优点:

    1. 与其他人相比,没有什么AFAIK。

    缺点:

    1. 4K限制是针对整个cookie,包括名称,值,到期date等。为了支持大多数浏览器,请将名称保留在4000字节以下,并将整体Cookie大小保持在4093字节以下。
    2. 数据被发送回服务器为每个HTTP请求(HTML,图像,JavaScript,CSS等) – 增加客户端和服务器之间的stream量。

      通常,允许以下内容:

      • 300个cookies
      • 每个cookie有4096个字节
      • 每个域名20个Cookie
      • 每个域81920字节 (给定20个最大大小4096 = 81920字节的cookie)
  3. 的sessionStorage

    优点:

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

    缺点:

    1. 数据仅在设置它的窗口/选项卡中可用。
    2. 数据不是持久的,即一旦窗口/标签closures,数据就会丢失。
    3. localStorage一样,tt工作在同源策略上 。 所以,存储的数据将只能在同一个来源。
  4. PostMessage的

    优点:

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

    缺点:

    1. 需要从当前窗口打开一个窗口,然后只能保持打开窗口才能进行通信。
    2. 安全性问题 – 通过postMessage发送string是,你将select由其他JavaScript插件发布的其他postMessage事件,所以一定要实现一个targetOrigin和健全性检查传递给消息监听器的数据。
  5. PostMessage + SessionStorage的组合

    使用postMessage在多个选项卡之间进行通信,同时在所有新打开的选项卡/窗口中使用sessionStorage来保存传递的数据。 只要标签页/窗口保持打开状态,数据将被保留。 所以,即使开启者选项卡/窗口被closures,打开的选项卡/窗口也会在刷新后获得全部数据。

我已经为此写了一个JavaScript库,命名为AcrossTabs ,它使用postMessage API在跨源标签页/窗口和sessionStorage之间进行通信,以便在打开的标签/窗口标识生存期间持久化。

人们应该考虑使用的另一种方法是共享工作者。 我知道这是一个尖端的概念,但是你可以在Shared Worker上创build一个比localstorage快得多的中继,并且不需要父/子窗口之间的关系,只要你在同一个源。

在这里看到我的答案,我做了一些讨论。

有一个微小的开源组件可以在基于localStorage的相同来源的标签页/窗口之间进行同步/通信(声明 – 我是贡献者之一!)。

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

https://github.com/jitbit/TabUtils

PS我冒昧地在这里推荐它,因为当事件几乎同时发生时,大部分“locking/互斥/同步”组件在WebSocket连接上失败

我在我的博客上写了一篇文章: http : //www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a-networking应用程序 。

使用我创build的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 

还有其他方便的方法来处理其他情况