HTML5 WebMessaging实验

分享于 

12分钟阅读

Web开发

  繁體 雙語

介绍

作为网络开发人员,有时我们很容易遇到一个问题: 跨域通信,符合 Same-Origin-Policy,JavaScript代码不能访问不同的域,或者协议( http/https ),或者端口,因此没有直接( 或者我可以说: 实现跨域通信的简单方法。 但是,这些类型的需求确实会发生: 第二页和第B 页位于不同的域,B 在A 中,换句话说,在一个页面中,第1 页是B,现在页面的URL是B,现在要控制第二页,反之亦然。

通过限制解决方案以 100%个客户端JavaScript完成,预先使用 HTML5,有许多冗长的"黑客攻击",例如:

  • URL长轮询:容器页面的iframe 页面改变了页面的URL,一旦哈希改变,它就会根据需要执行操作。 [BTW, the yf_str_pftf65dfojwws3tpnrxwo6k7obqxi5dfojxeai2aenacgudbor2gk4tol54wmx3umvzg22lon5wg6z3z_yf_str can be revised to be non-polling by HTML5 yf_link_hrqsa5djorwgkpjcn5xgqyltnbrwqylom5ssazlwmvxhiiranbzgkzr5ejuhi5dqhixs653xo4xhomzon5zgol2ukixwq5dnnq2s62djon2g64tzfzuhi3lmensxmzlooqwwqyltnbrwqylom5sseidumfzgozluhurf6ytmmfxgwir6_yfr_link onchashchange event ]
  • CrossFrame,跨文档和跨域的安全通信机制。
  • 窗口大小监视:更新大小的iframe 窗口,并且包含它的" onresize"事件,然后使用相应的action(s)。 Mapplets 采用这里 Pattern。

其实,我不喜欢所有的人。 要么是不雅的,要么违背了DOM元素的原始功能,或者过于复杂。 我相信很多人都不喜欢他们,即使是 Pattern的发明者。 这就是WHATWG在HTML5中创建跨域通信的原因: 网站消息

作为一个HTML5疯狂的advocator我非常喜欢它,完全的客户端通信,没有服务器影响,高效,安全的( 至少在理论上)。

如何

"父"可以是 iframe 或者弹出窗口,通过调用 window.open,"父页page包含类似 below的源代码:

<iframe id="ifr" src="http://domainB.com/B.htm" onload="sendCommand();">
 No frame!</iframe><script type="text/javascript"> 
 function sendCommand() {
 var ifr = document.getElementById("ifr");
 ifr.contentWindow.postMessage("Hello", "http://domainB.com");
 }
</script>

如果你在加载iframe时只在加载了的情况下发送消息,那么将仍然与容器页面位于同一域。

子页面B 包含 below 这样的代码:

<input type="button" value="Cross domain call" onclick="sendMsg();"/>
<script>
window.addEventListener("message", receiveMessage, false);function receiveMessage(evt) {
 console.log("Page B received message from origin: %s.", evt.origin);
 console.log("Event data: %s", evt.data);
 //evt.source will be a window who sent the message,//it can be used to post message to it// Take action(s)}
</script>

演示代码 上面 是一个 direction: 父级发送消息给子( iframe ),实际上双向消息传输也可以完成,类似于"父控件子控件"。子页面消息到容器窗口的唯一区别,只有调用" parent.postMessage"才可以。

function receiveMessage(evt) {
 evt.source.postmessage("Hello caller");
 // or parent.postmessage("Hello parent");}

网络消息传递必不可少

HTML5的消息,HTML5在浏览器窗口中显示消息事件,最终将消息传递到目标选项卡/窗口,如果目标选项卡/窗口尝试传递消息,最终会收到消息,最后是通过 MessageEvent.data 传递消息。

现场演示

我在我的dev机器上进行了演示,我重写了本地主机文件,让 Container.com, DomainA.com, DomainB.com 和 DomainC.com 都指向 127.0.0.1:

127.0.1.1 Container.com
127.0.0.1 DomainA.com
127.0.0.1 DomainB.com
127.0.0.1 DomainC.com

我准备了一个包含代码 below的Container 页面:

<h3>HTML5 Cross-Domain post message demo</h3><pid="infoBar"></p><divid="wrapperA"><inputtype="text"id="txtA"/><inputtype="button"value="Post Message"onclick="postMsgToIfr('A');"/><iframeid="ifrA"src="http://DomainA.com/A.htm"></iframe></div><divid="wrapperB"><inputtype="text"id="txtB"/><inputtype="button"value="Post Message"onclick="postMsgToIfr('B');"/><iframeid="ifrB"src="http://DomainB.com/B.htm"></iframe></div><divid="wrapperC"><inputtype="text"id="txtC"/><inputtype="button"value="Post Message"onclick="postMsgToIfr('C');"/><iframeid="ifrC"src="http://DomainC.com/C.htm"></iframe></div><divstyle="CLEAR: both"></div><scripttype="text/javascript"> window.addEventListener("message", receiveMessage, false);
 var infoBar = document.getElementById("infoBar");
 function receiveMessage(evt) {
 infoBar.innerHTML += evt.origin + ":" + evt.data + "";
 }
 function postMsgToIfr(domain) {
 switch (domain) {
 case"A":
 var ifr = document.getElementById("ifrA");
 ifr.contentWindow.postMessage(document.getElementById
 ("txtA").value, "http://DomainA.com");
 break;
 case"B":
 var ifr = document.getElementById("ifrB");
 ifr.contentWindow.postMessage(document.getElementById
 ("txtB").value, "http://DomainB.com");
 break;
 case"C":
 var ifr = document.getElementById("ifrC");
 ifr.contentWindow.postMessage(document.getElementById
 ("txtC").value, "http://DomainC.com");
 break;
 default:
 throw ("No such domain!");
 }
 } 
 </script>

下面的代码示例演示如何在 A.htm pages 中找到 cheat,并在浏览器中找到 physically physically C.htm physically physically physically physically physically similar similar similar

<h4>DomainA/A.htm1</h4><inputtype="button"value="Cross domain call"onclick="doClick();"/><divid="d"></div><script> window.addEventListener("message", receiveMessage, false);
 function doClick() {
 parent.postMessage("Message sent from" + location.host, "http://container.com");
 }
 var d = document.getElementById("d");
 function receiveMessage(evt) {
 d.innerHTML += "Received message"<span>" + evt.data + "</span>" from domain:" 
 + evt.origin + "";
 }
 </script>

我录制了一个GIF图像 below 来演示跨域消息传递:

HTML5 Cross-Domain Messaging Demo

查看在双向方向通过不同域的消息? 够 酷 吧

MessageChannel

为了在不同浏览上下文下支持独立通信,HTML5引入了消息通道 independently消息,它的正式定义是 below:

机制中的通信通道以双向管道方式实现,每个端口有一个端口。 在另一个端口发送的消息将传递到另一个端口,反之亦然。 消息是异步的,并作为DOM事件传递。

我花了半天时间研究消息通道,finally 使它正常工作,我的代码显示为 below。

容器页源代码
 <iframe id="ifr" src="http://wayneye.me/WebProjects/HRMS/Opener.html" 
 önload="initMessaging()"></iframe> <input type="button" value="Post Message" onclick="postMsg();"/>
<div id="d"></div> <script>
 var d = document.getElementById("d");
 var channel = new MessageChannel();
 channel.port1.onmessage = function (evt) {
 d.innerHTML += evt.origin + ":" + evt.data + "";
 };
 function initMessaging() {
 var child = document.getElementById("ifr");
 child.contentWindow.postMessage('hello', 'http://wayneye.me', [channel.port2]);
 }
 function postMsg() {
 channel.port1.postMessage('Message sent from ' + location.host);
 }
 </script>
iframe页源代码
<div id="info"></div> <input type="button" value="Post Message" önclick="postMsg();"/>
 <script>
 var info = document.getElementById("info");
 var port = null;
 window.addEventListener("message", function (e) {
 console.log(e);
 if(e.ports && e.ports.length> 0) {
 port = e.ports[0];
 port.start();
 port.addEventListener("message", function (evt) {
 info.innerHTML += "Received message"" + evt.data + "" 
 from domain:" + evt.origin + "";
 }, false);
 }
 }, false);
 function postMsg() {
 if(port) {
 port.postMessage("Data sent from" + location.host);
 }
 }
 </script>

整个过程可以描述为:

  • 容器页( A ) 嵌入了一个 iframe,它的src 指向不同域的页面( B )。
  • 加载 iframe 之后,容器将消息发送到页面B,并使用 MessagePortArray
  • 页B 接收到消息以及包含 MessagePort 对象列表的array。
  • 页B 在端口实例上注册 onmessage 事件。
  • 第B 页调用 port.postmessage 通过这里 MessageChannel 向第A 页发送消息。
  • 此时,我看来只有 Opera 正确支持 MessageChannel,Google Chrome 和IE10平台预览 2未能交付端口 array,Safari无法获取 onmessage 事件。 Firefox 7.0测试版不支持 MessageChannel 对象。
    注释:端口还可以在HTML5网络worker之间进行通信。

更重要的是,在一个值得注意的地方使用消息通道,网页开发人员应该明确关闭频道,否则,在两页之间就会有很强的引用,official official强调:

强烈建议作者显式关闭 MessagePort 对象以分解它们,以便它们的资源可以被 recollected。 创建许多 MessagePort 对象并放弃它们而不关闭它们可能会导致内存占用。

进一步阅读

最初发布在韦恩生命 http://WayneYe.com: http://wayneye.com/Blog/HTML5-WebMessaging-Experiment/。


WEB  EXP  Html5  WEBM  
相关文章