postMessage 和 MessageChannel

2,635 阅读1分钟

postMessage 是用来消息传递的重要api,在不同页面之间实现跨源通信方面起者重要作用。MessageChannel 允许我们创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据

1. 发送消息的主体需要与接收的主体保持一致

1.1 主页面发送消息给iframe

```jsx
// 主页面main.html
iframe.contentWindow.postMessage('I am from main page', '*')

//iframe.html
window.onmessage=function(e){
	console.log(e.data);
}

// 以上mainiframe.contentWindow 就是 iframe的window
```

1.2 iframe发送消息给主页面

```jsx
// iframe.html
window.parent.postMessage('I am from iframe page', '*')

// main.html
window.onmessage=function(e){
	console.log(e.data); // I am from iframe page
}
```

2. 借助MessageChannel 可以实现消息专用通道传输

2.1 主页面发送消息给iframe

```jsx
// 主页面main.html
var channel = new MessageChannel();
iframe.contentWindow.postMessage('I am from main page', '*', [channel.port1]);

//iframe.html
window.addEventListener('message', onMessage);
function onMessage(e) {
    console.log(e.data); // I am from main page
}

// iframe的onMessage 接收到的e.ports就是[channel.port1]
```

2.2 iframe发送消息给主页面

```jsx
// iframe.html
function onMessage(e) {
    console.log(e.data); // I am from main page
		e.ports[0].postMessage('I am from iframe page');
}

// main.html
var channel = new MessageChannel();
iframe.contentWindow.postMessage('I am from main page', '*', [channel.port1]);
channel.port2.onmessage = function(e){
	console.log(e.data); // I am from iframe page
}

// main.html 里prot1和port2可以互换,有点像一个土电话,随便拿起一头发消息,另外一头都能收到消息
```

3. 完整实例

3.1 main.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width">
    <title>main</title>
</head>

<body>
    <h1>Channel messaging demo</h1>
    <p class="output">My body</p>
    <iframe src="iframe.html" width='480' height='320'></iframe>
</body>
<script>
    var channel = new MessageChannel();
    var output = document.querySelector('.output');
    var iframe = document.querySelector('iframe');

    // Wait for the iframe to load
    iframe.addEventListener("load", onLoad);

    function onLoad() {
        // Listen for messages on port1  1-2可以互换
        // channel.port1.onmessage = onMessage;
        channel.port2.onmessage = onMessage;
        // 当iframe用window.parent发消息时可以通过window.onmessage来接收
        // window.onmessage = onMessage; 
        // Transfer port2 to the iframe
        // iframe.contentWindow.postMessage('I am from main page!', '*', [channel.port2]);
        iframe.contentWindow.postMessage('I am from main page!', '*', [channel.port1]);
    }

    // Handle messages received on port1
    function onMessage(e) {
        console.log(e, 'main');
        output.innerHTML = e.data + '---main--';
    }

</script>
</html>

3.2 iframe.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width">
    <title>iframe</title>
</head>

<body>
    <p class="output">iFrame body</p>
</body>
<script>
    var output = document.querySelector('.output');

    window.addEventListener('message', onMessage);

    function onMessage(e) {
        console.log(e, 'iframe');

        output.innerHTML = e.data;
        // Use the transfered port to post a message back to the main frame
        // window.parent.postMessage('I am from iframe page');
        e.ports[0].postMessage('I am from iframe page');
    }
</script>

</html>