5

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

一切正常,除了它可能会更好。

如果用户打开额外的选项卡/窗口,应用程序当前会重新加载所有数据,甚至可能会重复处理工作,这不是问题,只是它会减慢一切速度并且页面加载时间更长。

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

无论如何我可以将 iframe 的窗口引用传达给其他选项卡,以便他们可以通过 postMessage 与之通信?这可以通过使用共享工作者以某种方式实现吗?

我意识到我可以使用共享工作者来完成整个处理任务,但这会有它自己的问题,因为数据来自第三方域,无法从工作者内部访问。

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

编辑:我刚刚发现 SharedWorker 尚未在 Firefox 中实现,所以我想这不会起作用。我还有其他方法可以实现吗?

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

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

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

4

1 回答 1

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 事件将消息发送回任何正在处理数据的窗口来处理。但我还没有实现它,它可能不会在页面卸载之前完成。处理它的另一种方法是在检查标志并重试的辅助窗口中设置超时,我可能会走这条路线,因为消息已经附加了超时。

于 2012-06-13T14:05:43.070 回答