用postMessage解决跨域通信和跨域调用js问题

本文转自:https://bbs.implug.cn/?thread-3.htm
平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题:

  • 多窗口之间消息传递(newWin = window.open(…));
  • 页面与嵌套的iframe消息传递。
  • 同域名下可以通过parent调用父级页面的js跨域时如何调用。

这里介绍的方法是用运用postMessage方式解决
发消息方调用postMessage方法发送消息 postMessage是html5引入的API可以更方便、有效、安全的解决这些问题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
收消息方用MessageEvent监听消息。

postMessage(data,origin)方法接受两个参数

  • data:要传递的数据, html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
  • origin:字符串参数,指明目标窗口的源, 协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,someWindow.postMessage()方法只会在someWindow所在的源(url的protocol, host, port)和指定源一致时才会成功触发message
  • event,当然如果愿意也可以将参数设置为"*",someWindow可以在任意源,如果要指定和当前窗口同源的话设置为"/"。

MessageEvent的属性

  • data:顾名思义,是传递来的message
  • source:发送消息的窗口对象
  • origin:发送消息窗口的源(协议+主机+端口号)。

同域父子页面间通讯

代码示例:

父页面a.html:

    // localhost:9011/a.html
    <h1 class="header">page A</h1>
    <div class="mb20">
        <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
        <button style="font-size:20px;" onclick="send()">post message</button>
    </div>
    <!-- 不跨域的情况 -->
    <iframe src="b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>

    <script>
    function send() {//发送消息
        var data = document.querySelector('#data').value;
        // 注意: 只会触发当前window对象的message事件
        // 也可以访问子页面的window对象,触发子页面的message事件 (window.frames[0].postMessage(...))
        // window.postMessage(data, '/'); 
       // data = {name: 'sandy', age: 20, fav: {sing: true, shop: false}}; // 也可以传普通对象
        window.frames[0].postMessage(data, '/'); // 触发同域子页面的message事件(注意这里用的window.frames[0]代表发送消息给frames页面)
        //window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件
    }

    // 当前页面执行 window.postMessage(..)或子页面执行 parent.postMessage(...) 都会触发下面的回调, messageEvent.source不同而已
    window.addEventListener('message', function(messageEvent) {
        var data = messageEvent.data;// messageEvent: {source, currentTarget, data}
        console.info('message from child:', data);
    }, false);
    </script>

子页面b.html

    //> localhost:9011/b.html
    <h1 class="header">page B</h1>
    <input type="text" id="inp" value="some contents..">
    <button onclick="send()">send</button>

    <script>
    window.addEventListener('message', function(ev) {//监听接收消息 ev包含上面写道MessageEvent的三个属性
        if (ev.source !== window.parent) {return;}//这里通过判断消息来源来进行安全验证
        var data = ev.data;
        console.info('message from parent:', data);
    }, false);

    function send() { 
        var data = document.querySelector('#inp').value;
        //window.postMessage(data, '*'); // 发送消息触发当前页面的message事件(注意这里用的window代表发送给子页面)
        parent.postMessage(data, '*'); // 发送消息触发父页面的message事件(注意加上parent代表发送给父页面)
        //parent.postMessage(data,'http://localhost:9011/');//若父页面的域名和指定的不一致,则postMessage失败
    }
    </script>

跨域父子页面间通讯

父页面a.html:

   //> localhost:9011/a.html
   <h1 class="header">page A</h1>
   <div class="mb20">
       <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
       <button style="font-size:20px;" onclick="send()">post message</button>
   </div>
   <!-- 跨域的情况 -->
   <iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>

   <script>
   function send() {
       var data = document.querySelector('#data').value;

       window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件(window.frames[0]代表发送消息给frames[0]页面),这里的第二个参数表示可以接受此消息的页面的窗口的源origin如果天*带个所有。
   }

   window.addEventListener('message', function(messageEvent) {
       //接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
       var data = messageEvent.data; 
       console.info('message from child:', data);
   }, false);
   </script>

子页面b.html

    //> localhost:9022/b.html(两个页面端口号不同.为跨域的一种)
    <h1 class="header">page B</h1>
    <input type="text" id="inp" value="some contents..">
    <button onclick="send()">send</button>

    <script>
    window.addEventListener('message', function(ev) {
    //接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
        var data = ev.data;
        console.info('message from parent:', data);
    }, false);

    function send() {
        var data = document.querySelector('#inp').value;
        parent.postMessage(data, 'http://localhost:9011/'); //若父页面的域名和指定的不一致,则postMessage失败(parent代表发送消息给frames[0]页面)
        // parent.postMessage(data, '*'); // 触发父页面的message事件*代表任何页面都可接收,
    }
    </script>

通过上面的两个例子我们已经解决了,传递消息和跨域传递消息,那么怎么跨域调用JS呢。 原理很简单,就是把需要在另一个页面执行的js代码写成字符串用postMessage方法传递过去,然后另一个页面接收到后用eval()直接执行这段代码。

示例跨域情况下子页面让父页面alert(“hello!”);:

父页面a.html:

 //> localhost:9011/a.html
 <h1 class="header">page A</h1>
 <div class="mb20">
     <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
 </div>
 <!-- 跨域的情况 -->
 <iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>

 <script>
 window.addEventListener('message', function(messageEvent) {
     //接收消息的监听,这里可以通过ev.origin判断是否是合法的来源
     if(ev.origin='http://localhost:9022'){
         return;
     }
     eval(ev.data);
 }, false);
 </script>

子页面b.html

    //> localhost:9022/b.html(两个页面端口号不同.为跨域的一种)
    <h1 class="header">page B</h1>
    <input type="text" id="inp" value="some contents..">
    <button onclick="send()">send</button>

    <script>

    function send() {
        var data = 'alert("hello!");';
        parent.postMessage(data, '*'); // 触发父页面的message事件*代表任何页面都可接收,
    }
    </script>

本文转自:https://bbs.implug.cn/?thread-3.htm

猜你喜欢

转载自blog.csdn.net/u010454239/article/details/83687986
今日推荐