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

IT技术 javascript html web-applications local-storage postmessage
2021-01-27 02:02:30

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

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

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

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

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

例如,获得窗口 B 的引用并使用 window.postMessage() (HTML5) 会很好

最终目标是制作像 Facebook 这样的东西,如果你打开四个 Facebook 标签并在一个标签中聊天,那么每个 Facebook 标签中的聊天都是最新的,这很整洁!

6个回答

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

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

storage其他选项卡对 进行更改时会触发事件localStorage这对于通信目的非常方便。

参考资料可以在这里找到:
Webstorage
Webstorage - 存储事件

如果您尝试在嵌入某些父窗口的具有相同来源的 iframe 之间进行通信,那么这是 IE(即使在 IE11)goo.gl/jmFGzb中的一个痛苦的错误,它会破坏所有这种方法。站点的小部件就是一个很好的例子。
2021-03-17 02:02:30
如果您想要跨多个选项卡同步数据,我在这里写了一篇关于它的博客文章:ebenmonney.com/blog/... 。有了这个,您可以使用诸如 torageManager.saveSyncedSessionData('data', 'key' 之类的函数保存数据) 或 storageManager.savePermanentData('data', 'key') 并且您的数据将与所有打开的选项卡同步。它在下面使用 localStorage 和 sessionStorage
2021-03-26 02:02:30
使用 intercom.js:github.com/diy/intercom.js这是该方法的完整实现......
2021-03-31 02:02:30
我想这就是我想要的 HTML5 多屏游戏...
2021-04-01 02:02:30
2021-04-08 02:02:30

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)。
2021-04-06 02:02:30

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

不错,可惜似乎没有实施
2021-03-13 02:02:30
它在 moz、webkit 和我相信歌剧中实现。
2021-03-26 02:02:30
它没有在 Firefox 中实现:caniuse.com/sharedworkers
2021-03-31 02:02:30
根本不支持 IE,甚至 IE11。
2021-04-04 02:02:30

除了即将推出的SharedWorker 之外,您还可以使用支持更广泛的跨文档消息传递在这种情况下,必须有一个主窗口负责打开所有其他带有. 然后,子窗口可以使用的postMessage他们window.openwindow.opener

如果您可以选择使用 flash,那么任何安装了 flash 的客户端实际上都支持更旧的LocalConnection示例代码)。

其他回退方法:
用于 jQuery 的 postMessage 插件,带有 window.location.href 回退旧浏览器
基于 cookie 的非即时通信解决方案

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

你说你的:

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

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

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

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