JavaScript;具有相同来源的选项卡/窗口之间的通信[重复]

Posted

技术标签:

【中文标题】JavaScript;具有相同来源的选项卡/窗口之间的通信[重复]【英文标题】:JavaScript; communication between tabs/windows with same origin [duplicate] 【发布时间】:2011-01-15 06:17:11 【问题描述】:

我有两个窗口:窗口 A 和窗口 B。

窗口 A 和窗口 B 具有相同的域 窗口 A 和窗口 B 没有任何父窗口。
    窗口 A 是否可以获取窗口 B 的引用? 让窗口 A 通知窗口 B 的最优雅的方法是什么? (包括新的 html5 规范)

我知道这样做的两种方式:

服务器消息:窗口 B 定期询问服务器窗口 A 是否通知了某些内容 本地数据消息传递 (HTML5):当窗口 A 想要通知某事它更改了本地数据时,窗口 B 会定期检查本地数据是否有任何更改。

但是这两种方式都不是那么优雅。

例如,最好获取窗口 B 的引用并使用 window.postMessage() (HTML5)

最终目标是制作类似于 Facebook 的东西,如果您打开四个 Facebook 选项卡并在一个选项卡中聊天,则每个 Facebook 选项卡中的聊天都是最新的,这很整洁!

【问题讨论】:

My answer to 'Sharing websocket across browser tabs?' 可以帮到你。 ***.com/questions/19125823/… 目前 intercom.js 是选项卡之间共享单个套接字的最佳选择。 @Gothdo:如果在另一个问题之前已经问过,这怎么可能是重复的? @brillout.com meta.***.com/q/251938/3853934 【参考方案1】:

你说你的:

最终目标是制作类似于 facebook 的东西,如果你打开 4 个 facebook 标签,并在一个标签中聊天,聊天就会在每个 facebook 标签上实现,这很整洁!

这应该作为您设计的副产品发生,即视图查询模型(可能是服务器)以更新聊天,而不是您必须设计跨视图通信。除非您正在处理传输大量数据,否则为什么要担心呢?似乎它会使事情复杂化而没有巨大的收益。

几年前,我发现如果我使用现有窗口的名称和空白 URL 执行 window.open,我会得到对现有窗口的引用(这种行为甚至是 documented on MDN 和对 the MSDN docs 的评论建议它也适用于 IE;也就是说,2017 年有人在 MDN 文章中添加了a somewhat ranty note,列出了一些限制——我没有独立验证它们)。但那是几年前的事了,我不知道当今世界对它的支持有多普遍,当然,除非你所有的窗口都包含一个命名为iframe 用于通信,否则你不会有一个窗口名称可供查找,通过服务器端代码唯一命名,然后通过服务器端代码与其他窗口通信......(可怕的想法:这实际上可能是可行的。将与登录帐户相关的“当前”窗口名称存储在一个表格,将列表提供给任何创建的登录该帐户的新窗口,剔除旧的非活动条目。但如果列表稍微过时,您将在搜索其他人时打开新窗口......而且我敢打赌支持是从浏览器到浏览器的不确定性。)

【讨论】:

实际上这就是我基本上在做的事情:网络应用程序的整个状态都存储在浏览器的本地数据中。由于本地数据由窗口 A 和 B 共享,如果窗口 A 更改本地数据,窗口 B 只需重新读取本地数据即可获得新的状态。但是对于它,窗口 B 需要知道何时必须重新读取本地数据。一种方法是让窗口 B 定期检查本地数据。但是最好让窗口 A 告诉窗口 B“嘿,窗口 B,检查新状态并重新读取本地数据!”窗口参考的好代表好技巧 我刚刚尝试了结合window.postMessage()的技巧,即window.open(null,'windowName').postMessage('test msg',"*")。可悲的是似乎没有工作 不是null,空字符串;来自上面的 MDC 链接:“为 strUrl 提供一个空字符串是一种通过名称获取对打开窗口的引用而不更改窗口位置的方法。” MSDN 上的评论说同样的话。并不是说我会这样做,但是... 它似乎没有什么不同。问题是您的技巧似乎不适用于chrome。你知道这个把戏的名字吗?所以我可以研究谷歌浏览器的替代品。 @T.J. Crowder 啊,是的,我没有意识到这确实是几乎相同的链接,对不起。 (我试图#-link 尽可能直接地链接到该注释,here)。这在某种程度上是相当粗鲁的,但也充满了非常具体的问题/限制......我会假设(没有理由相信相反)实际上是正确的,无论语气如何。但我并不是要对您的回答不屑一顾:如果存在所讨论的限制不成问题的用例,它可能是一个非常好的解决方案(尤其是对于旧版浏览器支持)。【参考方案2】:

BroadcastChannel 标准允许这样做。目前它在 Firefox 和 Chrome 中实现(caniuse,mdn):

// tab 1
var ch = new BroadcastChannel('test');
ch.postMessage('some data');

// tab 2
var ch = new BroadcastChannel('test');
ch.addEventListener('message', function (e) 
    console.log('Message:', e.data);
);

【讨论】:

我可能错了,但似乎没有办法让窗口或选项卡专门回复 BroadcastChannel 消息发送者,是吗?我的意思是,如果 A 向 B、C、D、E 广播,那么 D 就无法与 A 发起私人对话。(event.source 参数表明应该有,但我在我的Firefox v78 调试器,它被设置为 null)。【参考方案3】:

我坚持使用localStorage 问题中提到的共享本地数据解决方案。就可靠性、性能和浏览器兼容性而言,它似乎是最佳解决方案。

localStorage 在所有现代浏览器中都实现了。

storage 事件在 other 选项卡更改 localStorage 时触发。这对于交流来说非常方便。

参考资料可以在这里找到:WebstorageWebstorage - storage event

【讨论】:

使用 intercom.js:github.com/diy/intercom.js 这是该方法的完整实现... 演示页面 - html5demos.com/storage-events 如果您尝试在嵌入到某些父窗口中的具有相同来源的 iframe 之间进行通信,这是 IE(即使在 IE11 中)goo.gl/jmFGzb 中的一个痛苦的错误。网站的小部件就是一个很好的例子。 如果您想要跨多个标签同步您的数据,我在这里写了一篇博客文章:ebenmonney.com/blog/…。有了它,您可以使用 torageManager.saveSyncedSessionData('data' , 'key') 或 storageManager.savePermanentData('data', 'key') 并且您的数据将与所有打开的选项卡同步。它在下面使用 localStorage 和 sessionStorage 我想这就是我想要的 HTML5 多屏游戏...【参考方案4】:

除了即将推出的SharedWorker,您还可以使用cross-document messaging,它更广泛地使用supported。在这种情况下,必须有一个主窗口负责用window.open 打开所有其他窗口。然后子窗口可以在其window.opener 上使用postMessage。

如果您可以选择使用闪存,那么在任何安装了闪存的客户端 (example code) 上实际上都支持更旧的 LocalConnection。

其他备用方法:postMessage plugin for jQuery with window.location.href fallback for older browsers cookie-based solution for non-instant communication

【讨论】:

所有窗口都从一个窗口打开的假设在很多情况下过于严格。在问题窗口A和窗口B中描述的场景中没有任何父窗口。 基于 flash 的方法和基于 cookie 的回退没有这个限制(显然,它们还有其他缺点)。 postMessage 仅在从原始窗口和框架、iframe 等打开第二个窗口时可用。如果您在不同的选项卡上具有相同的域,则它不支持。【参考方案5】:

SharedWorker 是 WHATWG/HTML5 规范,用于在选项卡之间进行通信的通用进程。

【讨论】:

不错,可惜好像没有实现 它在 moz、webkit 和我相信是歌剧中实现。 在 Firefox 中没有实现:caniuse.com/sharedworkers 完全不支持 IE,甚至 IE11。【参考方案6】:

我有一个巧妙的方法来做到这一点,但有一些限制:您应该允许您的域的弹出窗口,并且您将始终打开一个页面(作为选项卡或作为弹出窗口),这将实现窗口之间的通信。

这是一个例子: http://test.gwpanel.org/test/page_one.html (为域启用弹出窗口后刷新页面)

这个技巧的主要特点 - 弹出窗口最后是用 url 片段“#”打开的,这迫使浏览器不改变窗口位置并存储所有数据。 剩下的工作由 window.postMessage 完成。

【讨论】:

它似乎没有解决我的问题,但还是谢谢【参考方案7】:

AFAIK,如果它们没有相同的父级,就不可能跨窗口进行通信。

如果它们都从父窗口打开,您应该能够获取父窗口的变量引用。

在父级中,像这样打开窗口:

childA = window.open(...);
childB = window.open(...)

在 ChildA 中,像这样访问 childB:

childB = window.opener.childA

【讨论】:

问题仍然存在:让他们交流的最优雅的方式是什么?甚至是间接的,例如在服务器上。事实上,facebook 做到了,这是可能的。感谢您的代表! @romuwild:欢迎您使用代表 ;-) 看起来这是您的唯一选择(通过服务器)。您可能有兴趣知道 facebook 使用长轮询彗星,这是一种 http 交互,服务器不会终止连接,而是在可用时通过连接推送数据。见en.wikipedia.org/wiki/Comet_(programming)。 或通过本地数据而不是服务器。但是,是的,似乎是唯一的选择。谢谢你的链接,我不知道!

以上是关于JavaScript;具有相同来源的选项卡/窗口之间的通信[重复]的主要内容,如果未能解决你的问题,请参考以下文章

PayPal JavaScript 集成 - 打开新选项卡

在用户登录时,检查具有相同网站的其他标签并重新加载这些标签

使用 JavaScript 在窗口/选项卡之间进行通信 [重复]

JavaScript 在新窗口中打开,而不是选项卡

如何在javascript中检测浏览器选项卡是不是关闭或浏览器窗口

需要在 javascript 中为选项卡窗口和 Internet Explorer 中的新窗口提供唯一 ID [重复]