好吧,现在我必须把话锋调转一下了。前面讲的这么多,也只是抛出来一些之前我们可能会采用的跨域通信方法,事实上代理页面、url传参数和window.name、甚至还有一些利用url的hash值的跨域传值方法,都能百度到不少相关资料。但它们都逃不开代理页面,也就不可避免的要产生网络请求,而事实上这并不是我们的本意,我们原本希望它们能够直接在客户端通信,避免不必要的网络请求开销——这些开销,在访问量超大的站点可能会对服务器产生相当大的压力。那么,有没有更完美一点的替代方案呢? 必须给大家推荐postMessage。postMessage 正是为了满足一些合理的、不同站点之间的内容能在浏览器端进行交互的需求而设计的。利用postMessage API实现跨域通信非常简单,我们直接看一下实例的代码: 代码片段五,A.com/a.html:03 | < title >A.com/a.html</ title > |
04 | <script type="text/javascript"> |
05 | var trustedOrigin = "<a href=" http: |
07 | function messageHandler(e) { |
08 | if (e.origin == trustedOrigin) { |
15 | function sendString(s) { |
16 | document.getElementById( "widget" ).contentWindow.postMessage(s, trustedOrigin); |
19 | function showMsg(message) { |
20 | document.getElementById( "status" ).innerHTML = message; |
23 | function sendStatus() { |
24 | var statusText = document.getElementById( "statusText" ).value; |
25 | sendString(statusText); |
29 | addEvent(document.getElementById( "sendButton" ), "click" , sendStatus); |
33 | function addEvent(obj, trigger, fun) { |
34 | if (obj.addEventListener) obj.addEventListener(trigger, fun, false ); |
35 | else if (obj.attachEvent) obj.attachEvent( 'on' + trigger, fun); |
36 | else obj[ 'on' + trigger] = fun; |
38 | addEvent(window, "load" , loadDemo); |
39 | addEvent(window, "message" , messageHandler); |
46 | < input type = "text" id = "statusText" value = "msg from a.com/a.html" > |
47 | < button id = "sendButton" >向b.com/b.html发送消息</ button > |
48 | < p >接收到来自a.com/a.html的消息: < strong id = "status" ></ strong >.< p > |
代码片段六,B.com/b.html: 03 | < title >B.com/b.html</ title > |
04 | <script type="text/javascript"> |
07 | var trustedOrigin = "<a href=" http: |
08 | function messageHandler(e) { |
09 | if (e.origin === "<a href=" http: |
16 | function sendString(s) { |
17 | window.top.postMessage(s, trustedOrigin); |
21 | addEvent(document.getElementById( "actionButton" ), "click" , function () { |
22 | var messageText = document.getElementById( "messageText" ).value; |
23 | sendString(messageText); |
27 | function showMsg(message) { |
28 | document.getElementById( "status" ).innerHTML = message; |
31 | function addEvent(obj, trigger, fun) { |
32 | if (obj.addEventListener) obj.addEventListener(trigger, fun, false ); |
33 | else if (obj.attachEvent) obj.attachEvent( 'on' + trigger, fun); |
34 | else obj[ 'on' + trigger] = fun; |
36 | addEvent(window, "load" , loadDemo); |
37 | addEvent(window, "message" , messageHandler); |
43 | < p >接收到来自a.com/a.html的消息: < strong id = "status" ></ strong >.< p > |
45 | < input type = "text" id = "messageText" value = "msg from b.com/b.html" > |
46 | < button id = "actionButton" > 向a.com/a.html发送一个消息</ button > |
代码的关键是message事件是一个拥有data(数据)和origin(来源)属性的DOM事件。data属性是发送的实际数据,origin属性是发送来源。Origin属性很关键,有了这个属性,接收方可以轻易的忽略掉来自不可信源的消息,也就能有效避免跨域通信这个开口给我们的源安全带来的隐患。接口很强大,所以代码很简单。我们可以抓包看一下,这个通信过程完全是在浏览器端的,没有产生任何的网络请求。同时这个接口目前已经得到了绝大多数浏览器的支持,包括IE8及以上版本,参见下面的图表:

图7 但是为了覆盖ie6等低版本浏览器,我们完整的方案里面还是要包含一下兼容代码,就是最开始介绍的代理页面的方法了,但必须是以postMessage为主,这样即便最后会有某些浏览器因为这种通信产生一些网络请求,比例也是非常低的了。 |