html5: postMessage解決跨域和跨頁面通訊的問題
阿新 • • 發佈:2019-05-31
平時做web開發的時候關於訊息傳遞,除了客戶端與伺服器傳值,還有幾個經常會遇到的問題:
- 多視窗之間訊息傳遞(
newWin = window.open(..)
); - 頁面與巢狀的iframe訊息傳遞
postMessage方法
postMessage
是html5引入的API可以更方便、有效、安全的解決這些問題。postMessage()方法允許來自不同源的指令碼採用非同步方式進行有限的通訊,可以實現跨文字檔、多視窗、跨域訊息傳遞。
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].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>
跨域父子頁面間通訊
父頁面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.addEventListener('message', function(messageEvent) {
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) {
// 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事件
}
<