天天看點

[轉]html5: postMessage解決跨域和跨頁面通信的問題

平時做web開發的時候關于消息傳遞,除了用戶端與伺服器傳值,還有幾個經常會遇到的問題:

  1. 多視窗之間消息傳遞(

    newWin = window.open(..)

    );
  2. 頁面與嵌套的iframe消息傳遞

postMessage方法

postMessage

是html5引入的API可以更友善、有效、安全的解決這些問題。postMessage()方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實作跨文本檔、多視窗、跨域消息傳遞。

postMessage(data,origin)方法接受兩個參數

  1. data:要傳遞的資料,

    html5規範中提到該參數可以是JavaScript的任意基本類型或可複制的對象,然而并不是所有浏覽器都做到了這點兒,部分浏覽器隻能處理字元串參數,是以我們在傳遞參數的時候需要使用JSON.stringify()方法對對象參數序列化,在低版本IE中引用json2.js可以實作類似效果。

  2. 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].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) {
    // 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事件
    parent.postMessage(data, '*'); // 觸發父頁面的message事件
    // parent.postMessage(data, 'http://localhost:9011/'); // 若父頁面的域名和指定的不一緻,則postMessage失敗
}
</script>      

跨域父子頁面間通訊

//> 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.addEventListener('message', function(messageEvent) {
    var data = messageEvent.data; 
    console.info('message from child:', data);
}, false);
</script>      
//> 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) {
    // 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;
    parent.postMessage(data, 'http://localhost:9011/'); // 若父頁面的域名和指定的不一緻,則postMessage失敗
    // parent.postMessage(data, '*'); // 觸發父頁面的message事件
}
</script>