标签或窗口之间的通信
我正在寻找如何在浏览器(在同一个域,而不是CORS)的多个标签或窗口之间进行通信,而不留下痕迹。 有几个解决scheme:
- 使用窗口对象
- 的postMessage
- cookies
- 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的组合来使通信变得更加容易和可靠。
有不同的方法,每一个都有自己的优点和缺点。 让我们讨论一下:
-
localStorage的
优点 :
- Web存储可以被简单地看作是对cookie的改进,提供更大的存储容量。 如果您查看Mozilla源代码,我们可以看到5120KB ( 5MB ,相当于Chrome上的250万个字符 )是整个域的默认存储大小。 与典型的4KB Cookie相比,这样可以为您提供更多的空间。
- 数据不会被发送回服务器,每个HTTP请求(HTML,图像,JavaScript,CSS等) – 减less客户端和服务器之间的通信量。
- 存储在localStorage中的数据一直存在,直到被明确删除。 所做的更改已保存,并可用于当前和未来对该网站的所有访问。
缺点 :
- 它在同源策略上工作 。 所以,存储的数据将只能在同一个来源。
-
cookies
优点:
- 与其他人相比,没有什么AFAIK。
缺点:
- 4K限制是针对整个cookie,包括名称,值,到期date等。为了支持大多数浏览器,请将名称保留在4000字节以下,并将整体Cookie大小保持在4093字节以下。
-
数据被发送回服务器为每个HTTP请求(HTML,图像,JavaScript,CSS等) – 增加客户端和服务器之间的stream量。
通常,允许以下内容:
- 共300个cookies
- 每个cookie有4096个字节
- 每个域名20个Cookie
- 每个域81920字节 (给定20个最大大小4096 = 81920字节的cookie)
-
的sessionStorage
优点:
- 它与
localStorage
类似。 - 更改仅适用于每个窗口(或Chrome和Firefox等浏览器中的选项卡)。 所做的更改将保存并可用于当前页面,以及将来在同一窗口中访问该网站。 窗口closures后,存储被删除
缺点:
- 数据仅在设置它的窗口/选项卡中可用。
- 数据不是持久的,即一旦窗口/标签closures,数据就会丢失。
- 像
localStorage
一样,tt工作在同源策略上 。 所以,存储的数据将只能在同一个来源。
- 它与
-
PostMessage的
优点:
- 安全地启用跨源通信。
- 作为一个数据点,WebKit实现(由Safari和Chrome使用)目前没有强制执行任何限制(除了内存不足外)。
缺点:
- 需要从当前窗口打开一个窗口,然后只能保持打开窗口才能进行通信。
- 安全性问题 – 通过postMessage发送string是,你将select由其他JavaScript插件发布的其他postMessage事件,所以一定要实现一个
targetOrigin
和健全性检查传递给消息监听器的数据。
-
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
还有其他方便的方法来处理其他情况
- JS li标记onclick不工作在IE8上
- 我怎样才能确定浏览器实际使用什么字体来渲染一些文本?
- 在窗口closures或页面刷新上运行JavaScript代码?
- “未定义”自2012年6月12日起随机附加在我网站上所请求的url的1%之内
- 禁用浏览器上的返回button
- 如何在HTTP中编码Content-Disposition头的filename参数?
- 避免JSF Web应用程序上的后退button
- Python中的浏览器:如何selectBrython,PyPy.js,Skulpt和Transcrypt?
- 如何修复JavaScript的Internet Explorer浏览器中的Array indexOf()