HTML5 Web 通信(Web Messaging)

最后更新:
阅读次数:

首先,我们先来介绍一下 message 事件。因为,无论是跨文档通信(cross-document messaging)通道通信(channel messaging)服务器发送事件(server-sent events)或是网络套接字(web sockets)都要执行 message 事件。

  • 该事件的事件对象 event 有以下 5 个属性:
    • event.data: 返回接收到的数据(for all)
    • event.origin: 返回数据发送方的源(for server-sent events and cross-document messaging)
    • event.lastEventId: Returns the last event ID string(for server-sent events)
    • event.source: 返回数据发送方的窗口对象的引用(for cross-document messaging)
    • event.ports: Returns the MessagePort array sent with the message(for cross-document messaging and channel messaging)

跨文档通信(cross-document messaging)

  • 核心 API: Window.postMessage()

  • 基本语法: windowObj.postMessage(message, targetOrigin, [transfer])

    • windowObj:其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames
    • message:即将被发送的数据
    • targetOrigin:指定接收消息窗口的源(origin)
    • transfer:可选值,是一串和 message 同时传递的 Transferable 对象。这个参数一般在通道通信里使用,我们后边再讲。

在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送。

注意: targetOrigin 是一个源,不要写多余的东西(比如下面的 /路径
要这样写:windowObj.postMessage(‘Hello’,’https://movie.douban.com‘);
不要这样写:windowObj.postMessage(‘Hello’,’https://movie.douban.com/‘);

下面是我测试过的代码,如果你想用下面代码自己操作一下,请严格按照下面代码注释中标明的步骤进行操作(将代码复制粘贴于对应页面的浏览器控制端运行即可)。

  • 测试一:与 window.open 打开的异源页面进行通信
当前页面:http://blog.percymong.com/

// 步骤一,获得窗口对象
var windowObj = window.open('https://movie.douban.com/','open page');

// 步骤二,设置 message 事件监听
window.onmessage=function(e){
alert('Return Message Get');
console.log(e.data);
console.log(e.origin);
console.log(e.source);
};

// 步骤四,发送消息
windowObj.postMessage('我是percy,你好豆瓣','https://movie.douban.com');

/**************************************************************/

新打开的页面:https://movie.douban.com/

// 步骤三,设置 message 事件监听,并向发送消息窗口返回消息
window.onmessage=function(e){
alert('Message Get');
console.log(e.data);
console.log(e.origin);
console.log(e.source);
// 返回消息
e.source.postMessage('我是豆瓣,你好percy','http://blog.percymong.com')
};
  • 测试二:与 iframe 创建的异源页面通信
这里为了方便,我利用了本地服务器里进行测试

当前页面:http://blog.percymong.com/

// 步骤一,强制给当前页面写入一个 iframe 框架
var iframe = '<iframe src="http://localhost/test/test1.html" width="600" height="300"></iframe>';
document.body.innerHTML = iframe;

// 步骤二,为当前页面设置 message 事件监听
window.onmessage = function(e){
alert(e.data);
};

// 步骤四,发送消息
window.frames[0].postMessage('Message from blog.percymong.com','http://localhost');

/**************************************************************/

测试页面:http://localhost/test/test1.html

// 步骤三,编写好测试页面,并在文件中写入以下脚本
window.onmessage = function(event) {
alert(event.data);
event.source.postMessage('消息已收到 , from localhost', 'http://blog.percymong.com');
};

通过 window.postMessage,也能使不同窗口的 LocalStorage 进行交互。

通道通信(channel messaging)

通道通信暂时还没怎么用过,以后了解的多了,再来完善这部分。

Channel messaging provides a means of direct, two-way communication between browsing contexts.

通道通信多用于多个源之间的通信。

When we create a MessageChannel object, we are really creating two interrelated ports. One port stays open on our sending side. The other is forwarded to another browsing context.

  • 新建一个 MessageChannel 对象,实际上就创造了两个相关联的端口,一个端口在发送方这边并保持开放,另一个被传送到另一个浏览上下文。

    • MessageChannel.port1: 返回端口 1
    • MessageChannel.port2: 返回端口 2
  • 上面两个相关联的端口实际上都是一个 MessagePort 对象,而这个对象有 3 个方法

    • MessagePort.postMessage(message[, transferList]): 通过已经创建的通道发送消息
    • MessagePort.start(): Begins the dispatch of messages received on the port
    • MessagePort.close(): Closes and deactivates the port
    • MessagePort 对象也有一个 message 事件,用来监听消息的传递(MessagePort.onmessage)

下面做了一个简单的例子来练一下上面的语法。

这里为了方便,我也利用了本地服务器里进行测试

当前页面:http://blog.percymong.com/

// 步骤一,强制给当前页面写入一个 iframe 框架
var iframe = '<iframe src="http://localhost/test/test.html" width="600" height="300"></iframe>';
document.body.innerHTML = iframe;

// 步骤二,新建 MessageChannel,并为当前页面端口设置 message 事件监听
var channel = new MessageChannel();
var frame = window.frames[0];
channel.port1.onmessage = function(event){
alert(event.data);
};

// 步骤四,点击发送消息
document.body.onclick=function(){
frame.postMessage('A message , from percy', 'http://localhost',[channel.port2]);
};

/**************************************************************/

测试页面:http://localhost/test/test1.html

// 步骤三,编写好测试页面,并在文件中写入以下脚本
window.onmessage = function(event) {
alert(event.data);
console.log(event.source);
console.log(event.origin);
event.ports[0].postMessage('消息已收到 , from localhost test1.html');
};

对于上面的例子,我有一个小疑问,为什么消息只能发送一次(点击页面发送消息),第二次发送会报错?

参考资料