如何检查已打开的跨域窗口是否准备好接收 postmessage?

4
我从另一个域名启动一个URL,然后对其进行postMessage。
const child = window.open("http://urlfromanotherdomain.com");
child.postMessage("you cant handle the message", "*");

在我启动的子窗口中,JavaScript代码通过以下方式注册其兴趣:
window.onmessage = function(ev) {window.console.log(ev);}

问题在于有时候在子窗口运行很多代码之前,它并不能接收到发来的消息。
如何可靠地知道跨域子窗口已准备好接收消息?我试过用child.onload注册,但由于是跨域,它并不会触发。是否还有其他方法,例如传递参数给window.open,以保证消息的传递?
可能相关的其他信息:
- 我正在使用Chrome 62版本。 - 我最终得到了一个跨域窗口的真正原因是我想确保在子窗口中获得一个新的渲染线程,因为渲染非常耗费资源。如果我让它与父窗口相同的来源,则Chrome似乎会重用渲染进程,从而使用相同的线程。

3
你能控制urlfromanotherdomain.com吗?如果可以的话,考虑发出一个HTML5的“准备就绪”消息到window.opener。 - Maluen
你所说的 HTML5 "ready" 是指 opener.postMessage 吗?在我使用 Chrome 进行测试时,我得出结论 child.opener = null 可以确保新窗口拥有自己的进程,但现在我无法复现。我会尝试一下这个方法。 - hawk
1个回答

1
我发现没有直接的方法来实现这个。难点在于无法监听跨域窗口上的事件,因此没有直接的方法来知道何时完成加载并准备好接收您发布的消息。
一个快速的解决方法是使用超时:
 var child = window.open( url, winId );
 setTimeout( function(){
     child.postMessage(data, url);
 }, 1000);

当然,如果孩子加载速度真的很慢,这种方法并不能保证成功。
如果你可以访问孩子代码,那么你可以使用上面提到的“setInterval”,并不断调用postMessage,直到孩子使用“event.source.postMessage()”返回一条消息,如MDN页面中所述。父级也需要设置一个消息监听器。一旦父级收到回复消息,就清除间隔。我还会设置一个限制,使间隔在一定尝试次数后停止运行。
如果你无法访问孩子代码,还有另一种方法,涉及使用iFrame。一旦你拥有一个iframe,即使它从不同的域加载,你也可以知道它何时完成加载。这就是我让它工作的方法:
  1. 创建一个新的HTML页面,包含JS、CSS和HTML,像这样命名为“child-container.html”:

window.loadChildApp = function(url, json, title){
            window.document.title = title;
            var iframe = document.querySelector('#myIframe');
            iframe.src = url;
            document.querySelector('#loadingDiv').style.display = 'none';
            iframe.addEventListener('load', function(){
                iframe.contentWindow.postMessage(JSON.stringify(json), url);
            });
        }
#myIframe{
        height: 100%;
        width: 100%;
        border: none;
        margin: none;
    }
    
     #loadingDiv{
        height: 100%;
        width: 100%;
        margin: auto;
        padding: none;
    }
<div id="loadingDiv">Loading ... </div>
<iframe id="myIframe"  >

  1. In your parent page, open the child-container html, attach a load listener. When it loads you call the loadChildApp function, which will do the real work of loading the iframe, waiting for it to finish, then posting the message.

        let win = window.open( 'child-container.html', key );
        win.addEventListener('load', function () {
            win.loadChildApp(url, data, title);
        });
    
这种技术的一个小缺点是,用户看不到子页面真正的标题和网址(在地址栏中显示)。他们只会看到你给出的标题和 child-container.html 的网址。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接