在同一域上的不同窗口之间进行通信

时间:2012-06-12 12:18:10

标签: javascript postmessage

我正在构建一个执行大量客户端数据下载和处理的应用程序。通过在驻留在子域上的iframe中处理,数据处理与主应用程序隔离。这是iframe下载数据。通过postMessage进行沟通。

一切正常,但可能会更好。

如果用户打开额外的标签/窗口,该应用程序当前会重新加载所有数据,甚至可能会执行重复的处理工作,这不是一个问题,除了它减慢所有内容并且页面加载时间更长。

我想要做的是让每个顶级选项卡/窗口只与一个处理iframe通信,如果原始窗口关闭,可以恢复。问题是,这些不是通过javascript打开的,而是通过普通的浏览器方法打开标签中的链接,这样我就无法获得发送消息所需的iframe的引用。

无论如何我可以将iframe的窗口引用传递给其他选项卡,以便它们可以通过postMessage与它通信吗?这可以通过共享工作人员实现吗?

我意识到我可以将共享工作者用于整个处理任务,但这会产生问题,因为数据来自第三方域,无法从工作人员中访问。

只需要与所有主流浏览器的最新版本兼容。

编辑:我刚刚发现在Firefox中尚未实现SharedWorker,所以我想这不会起作用。我能用其他任何方式实现这个目标吗?

编辑2:我发现你可以使用:

var win = window.open('', 'my_window_name'); 

从任何其他窗口捕获对iframe的引用。但是,如果iframe尚不存在,则会将其作为窗口打开。即使它立即关闭,也会导致闪烁并导致“弹出窗口被阻止”消息,使其无法使用。

1 个答案:

答案 0 :(得分:3)

如果其他任何人发现这一点,我会想出一个解决方案。它有点hacky,需要进一步测试。但到目前为止它正在发挥作用。如果需要,它可以跨域工作。

它使用了两种技巧的组合。

首先是使用

remote_window = window.open("", "remote_window_name");

获取对窗口的引用。这是有效的,因为如果窗口已经打开并具有给定名称,则会返回一个引用而不是打开一个新窗口。

然而,它确实存在如果iframe不存在则会弹出一个新窗口的问题。使用本地存储是为了防止这种情况。加载窗口/选项卡时,它会检查localStorage以查看是否已存在具有共享iframe的其他页面。如果没有,则插入iframe并在本地存储中设置一个标志,表示它可用。

作为最后抛弃的度假胜地,如果窗口仍然打开,则使用try块关闭新打开的窗口。 try块可防止跨域错误。这意味着最糟糕的情况是用户会看到一个窗口弹出并消失,或者他们会看到“启用弹出窗口”消息。我还没有设法在测试中触发这一点 - 这只是一个边缘情况下降。

try {
    if(store_window.location.href === "about:blank" ){
        remote_window.close();
        remote_window = insertIfame();
    }
} catch(err) {
}

添加onunload事件,在页面关闭时删除该标志。

还会创建一个不断刷新超时标志的setInterval。我让它每秒运行4次;当加载第二个窗口/选项卡时,它会在尝试与之通信之前检查iframe标志是否未超时。这是一个很小的开销,但远远低于我第二次加载iframe的成本。如果浏览器崩溃或onunload因任何原因未触发,则用于防止过时数据。在检查超时时我包含一个小余地 - 当前为1秒 - 以防主窗口陷入循环。余地仅在超时时,而不是完全移除旗帜的卸载事件。

每次发送邮件时都需要检查标记,以防iframe的原始窗口关闭。发生这种情况时,iframe会重新插入需要它的第一个打开的窗口,并重置标志。

发回信息很简单。只需使用receiveMessage的event.source属性 - 它指向发送窗口。

要考虑的最后一个边缘情况是主窗口是否关闭,而iframe是辅助窗口的中间过程。从理论上讲,这可以通过在iframe中使用onunload事件来处理,以便将消息发送回任何正在处理数据的窗口。但我还没有实现它,它可能无法在页面卸载之前完成。处理它的另一种方法是在辅助窗口中超时检查标志并重试,我可能会在消息已经附加超时的情况下走这条路。