HTML5 postMessage 和 onmessage API 具体应用

it2025-06-22  14

HTML5 postMessage 和 onmessage API 具体应用

随着 HTML5 的发展。了解并熟悉 HTML5 的 API 接口是很重要的。postMessage(send) 和 onmessage 此组 API 在 HTML5 中有着广泛的应用,比方 Web Workers 中应用此组 API 实现多个线程间 JavaScript 调用功能 ,Cross-document messaging 中实现两个不同域间 JavaScript 调用功能等等。本文主要介绍此组 API 在 Web Workers,Cross-document messaging。WebSockets 以及 Server-Sent Events 中的具体应用情况。

姜 俊杰, 软件project师, IBM

2013 年 1 月 10 日

内容

Web Workers

Web Workers 简单介绍

至 2008 年 W3C 制定出第一个 HTML5 草案開始。HTML5 承载了越来越多崭新的特性和功能。它不但强化了 Web 系统或网页的表现性能,并且还添加了对本地数据库等 Web 应用功能的支持。

当中,最重要的一个便是对多线程的支持。在 HTML5 中提出了工作线程(Web Workers)的概念。并且规范出 Web Workers 的三大主要特征:可以长时间执行(响应),理想的启动性能以及理想的内存消耗。Web Workers 同意开发者编写可以长时间执行而不被用户所中断的后台程序,去执行事务或者逻辑,并同一时候保证页面对用户的及时响应。

Web Workers 为 Web 前端网页上的脚本提供了一种能在后台进程中执行的方法。一旦它被创建,Web Workers 就行通过 postMessage 向任务池发送任务请求。执行完之后再通过 postMessage 返回消息给创建者指定的事件处理程序 ( 通过 onmessage 进行捕获 )。

Web Workers 进程可以在不影响用户界面的情况下处理任务。而且,它还可以使用 XMLHttpRequest 来处理 I/O,但通常,后台进程(包含 Web Workers 进程)不能对 DOM 进行操作。假设希望后台程序处理的结果可以改变 DOM,仅仅能通过返回消息给创建者的回调函数进行处理。

浏览器对 HTML5 支持情况能够參考站点 When can I use...

在 Web Workers 中使用 postMessage 和 onmessage

首先,须要在client页面的 JavaScript 代码中 new 一个 Worker 实例出来,參数是须要在还有一个线程中执行的 JavaScript 文件名。然后在这个实例上监听 onmessage 事件。

最后还有一个线程中的 JavaScript 就能够通过调用 postMessage 方法在这两个线程间传递数据了。

清单 1. 主线程中创建 Worker 实例,并监听 onmessage 事件
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Test Web worker</title> <script type="text/JavaScript"> function init(){ var worker = new Worker('compute.js'); //event 參数中有 data 属性,就是子线程中返回的结果数据 worker.onmessage= function (event) { // 把子线程返回的结果加入到 div 上 document.getElementById("result").innerHTML += event.data+"<br/>"; }; } </script> </head> <body οnlοad="init()"> <div id="result"></div> </body> </html>

在client的 compute.js 中,仅仅是简单的反复多次加和操作,最后通过 postMessage 方法把结果返回给主线程,目的就是等待一段时间。而在这段时间内,主线程不应该被堵塞,用户能够通过拖拽浏览器,变大缩小浏览器窗体等操作測试这一现象。这个非堵塞主线程的结果就是 Web Workers 想达到的目的。

清单 2. compute.js 中调用 postMessage 方法返回计算结果
var i=0; function timedCount(){ for(var j=0,sum=0;j<100;j++){ for(var i=0;i<100000000;i++){ sum+=i; } } // 调用 postMessage 向主线程发送消息 postMessage(sum); } postMessage("Before computing,"+new Date()); timedCount(); postMessage("After computing,"+new Date());
图 1. 浏览器中执行结果

Cross-document messaging

Cross-document messaging 简单介绍

因为同源策略的限制。JavaScript 跨域的问题。一直是一个颇为棘手的问题。

HTML5 提供了在网页文档之间互相接收与发送信息的功能。使用这个功能,仅仅要获取到网页所在窗体对象的实例。不仅同源(域 + port号)的 Web 网页之间能够互相通信,甚至能够实现跨域通信。 要想接收从其他窗体发送来的信息,必须对窗体对象的 onmessage 事件进行监听,其他窗体能够通过 postMessage 方法来传递数据。该方法使用两个參数:第一个參数为所发送的消息文本,但也能够是不论什么 JavaScript 对象(通过 JSON 转换对象为文本),第二个參数为接收消息的对象窗体的 URL 地址,能够在 URL 地址字符串中使用通配符'*'指定所有地。

在 Cross-document messaging 中使用 postMessage 和 onmessage

为了实现不同域之间的通信,须要在操作系统的 hosts 文件加入两个域名。进行模拟。

清单 3. hosts 文件里加入两个不同的域名
127.0.0.1 parent.com 127.0.0.1 child.com

在父网页中通过 iframe 嵌入子页面。并在 JavaScript 代码中调用 postMessage 方法发送数据到子窗体。

清单 4. 父页面中嵌入子页面,调用 postMessage 方法发送数据
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Test Cross-domain communication using HTML5</title> <script type="text/JavaScript"> function sendIt(){ // 通过 postMessage 向子窗体发送数据 document.getElementById("otherPage").contentWindow .postMessage( document.getElementById("message").value, "http://child.com:8080" ); } </script> </head> <body> <!-- 通过 iframe 嵌入子页面 --> <iframe src="http://child.com:8080/TestHTML5/other-domain.html" id="otherPage"></iframe> <br/><br/> <input type="text" id="message"><input type="button" value="Send to child.com" οnclick="sendIt()" /> </body> </html>

在子窗体中监听 onmessage 事件,并用 JavaScript 实现显示父窗体发送过来的数据。

清单 5. 子窗体中监听 onmessage 事件,显示父窗体发送来的数据
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Web page from child.com</title> <script type="text/JavaScript"> //event 參数中有 data 属性。就是父窗体发送过来的数据 window.addEventListener("message", function( event ) { // 把父窗体发送过来的数据显示在子窗体中 document.getElementById("content").innerHTML+=event.data+"<br/>"; }, false ); </script> </head> <body> Web page from http://child.com:8080 <div id="content"></div> </body> </html>
图 2. 父窗体嵌入子窗体
图 3. 父窗体发送数据到子窗体

WebSockets

WebSockets 简单介绍

在 Web 应用中,HTTP 协议决定了client和服务端连接是短连接,即client Request,服务端 Response。连接断开。要想实现client和服务端实时通信,仅仅能通过client轮询来实现。服务端推送数据也并非字面上意思上的直接推。事实上还是client自己取。WebSockets 是 HTML5 规范新引入的功能。用于解决浏览器与后台server双向通讯的问题,使用 WebSockets 技术,后台能够随时向前端推送消息,以保证前后台状态统一。

在 WebSockets 中使用 send 和 onmessage

因为文本主要介绍 postMessage(send) 和 onmessage client API 的应用,而 WebSockets 涉及到server端代码的实现,所以本文将选取最简单的server端框架来编写server代码。WebSockets server端有 jetty 提供的基于 Java 的实现。有 WebSocket-Node 基于 node.js 的实现,在 .Net 4.5 中也直接提供了 WebSockets 的支持。本文将使用 WebSocket-Node 提供的演示样例代码,稍作改动作为 WebSockets 的server端。

关于 node.js 的介绍以及使用请參考 node.js 官方站点 node.js,关于 WebSocket-Node 的使用请參考 WebSocket-Node。

首先。须要在client通过 JavaScript 代码 new 一个 WebSocket 实例出来,參数是实现 WebSocket server端 URL 地址。然后在这个实例上监听 onmessage 事件接收server端发送过来的数据。当然。client也能够调用 send 方法,发送数据到server端。

清单 6. 创建 WebSocket 对象。并监听 onmessage 事件
connect : function() { var location ="ws://localhost:8000/"; // 创建 WebSockets 并传入 WebSockets server 地址 this._ws =new WebSocket(location); this._ws.onmessage=this._onmessage; //WebSockets 还提供了 onopen 以及 onclose 事件 this._ws.onopen =this._onopen; this._ws.onclose =this._onclose; }

在 _onmessage 方法中,接收数据,并显示在页面上

清单 7. _onmessage 方法
_onmessage : function(event) { //event 參数中有 data 属性。就是server发送过来的数据 if (event.data) { var messageBox = document.getElementById('messageBox'); var spanText = document.createElement('span'); spanText.className ='text'; // 把server发送过来的数据显示在窗体中 spanText.innerHTML = event.data; var lineBreak = document.createElement('br'); messageBox.appendChild(spanText); messageBox.appendChild(lineBreak); messageBox.scrollTop = messageBox.scrollHeight - messageBox.clientHeight; } },

在 _onopen 方法中,调用 _send 方法发送一条消息到server端,告之连接已经建立。在 _onclose 方法中,把 WebSocket 的实例设置成 null,释放资源。

清单 8. _onopen,_onclose 以及 send 方法
_onopen : function() { server._send("Client:Open WebSockets,"+new Date()); }, //message 參数就是client向服务器端发送的数据 _send : function(message) { if (this._ws) this._ws.send(message); }, // 此方法提供外部代码调用 send : function(text) { if (text !=null&& text.length >0) server._send(text); }, _onclose : function(m) { this._ws =null; }

把这些方法封装在一个 server 对象中。方便提供外部调用。用户仅仅须要先调用 server 的 connect 方法建立连接,然后调用 send 方法发送数据。

清单 9. 封装client实现
var server = { // 对外主要提供 connect 和 send 方法 connect : function() {...}, _onopen : function() {...}, _send : function(message) {...}, send : function(text) {...}, _onmessage : function(event) {...}, _onclose : function(m) {...} };

在服务器端,通过 JavaScript 语言简单改动 WebSocket-Node 中提供的 echo-server.js 演示样例就可以。

这里仅仅展示关键代码部分,其他代码请參见 WebSocket-Node 演示样例。

清单 10. WebSockets server端简单实现
// 监听client的连接请求 wsServer.on('connect', function(connection) { function sendCallback(err) { if (err) console.error("send() error: " + err); } // 监听client发送数据的请求 connection.on('message', function(message) { if (message.type === 'utf8') {// 差别client发过来的数据是文本还是二进制类型 connection.sendUTF( "Server:Get message:<br/>"+message.utf8Data, sendCallback ); } else if (message.type === 'binary') { connection.sendBytes(message.binaryData, sendCallback); } }); connection.on('close', function(reasonCode, description) { }); });
图 4. 点击 Connect button
图 5. 输入内容,单击 Send Message button

Server-Sent Events

Server-Sent Events 简单介绍

HTML5 Server-Sent 事件模型同意您从server push 实时数据到浏览器。本文我们将介绍利用 Eventsource 对象处理与页面间的接收和发送数据。在client。我们使用 HTML5+JavaScript。服务端使用 Java。在现存的 Ajax 模式中,web 页面会持续不断地请求server传输新数据,由client负责请求数据。而在服务端发送模式下。无需在client代码中运行连续的数据请求。而是由服务端 push 推送更新。一旦您在页面中初始化了 Server-Sent 事件。服务端脚本将持续地发送更新。

client JavaScript 代码一旦接收到更新就将新的数据写入页面中展示出来。

在 Server-Sent Events 中使用 onmessage

Server-Sent Events 和 WebSockets 有相同之处,WebSockets 实现了server端以及client的双向通信功能,而 Server-Sent Events 则仅是指server端到client的单向通信。并且 Server-Sent Events 相同须要server端的实现。本文将使用基于 Java 的 Servlet 技术实现server端。

关于server端向client写数据的格式。能够參考 W3C 关于 Server-Sent Events 的规范文档 Server-Sent Events。因为是server端到client的单向通信。所以在 Server-Sent Events 中没有 postMessage 方法。

首先,在client通过 JavaScript 代码 new 一个 EventSource 实例出来,參数是实现 EventSource server端 URL 地址。

然后在这个实例上监听 onmessage 事件接收server端发送过来的数据。

清单 11. 创建 EventSource 对象,并监听 onmessage 事件
if (!!window.EventSource) { // 创建 EventSource 实例,传入 server 地址 var source = new EventSource('/TestHTML5/ServerSentEvent'); } else { console.log("Your browser doesn't support server-sent event"); } // 监听 message 事件,等待接收服务器端发送过来的数据 source.addEventListener('message', function(event) { //event 參数中有 data 属性,就是服务器发送过来的数据 console.log(event.data); }, false); //EventSource 还提供了 onopen 以及 onerror 事件 source.addEventListener('open', function(event) { }, false); source.addEventListener('error', function(event) { if (event.readyState == EventSource.CLOSED) { } }, false);

server端,在 Java 语言实现的 Servlet doGet 方法中使用 response 对象向client写数据

清单 10. server端简单实现
// 这里必须设置 Content-Type 为 text/event-stream response.setHeader("Content-Type", "text/event-stream"); response.setHeader("Cache-Control", "no-cache"); response.setCharacterEncoding ("UTF-8"); String id = new Date().toString(); response.getWriter().println("id:"+id); // 向client写两行数据 response.getWriter().println("data:server-sent event is working."); response.getWriter().println("data:test server-sent event multi-line data"); response.getWriter().println(); response.getWriter().flush();
图 6. Server-Sent Events 执行结果

结束语

本文具体介绍了 postMessage(send)和 onmessage API 在client的应用情况。能够看到在不同的场景中这两个方法的应用模式都是类似的。

postMessage 的作用就是传递数据,而 onmessage 的作用就是接收数据。

掌握此组 API 对以后开发 HTML 5 应用程序将会有所帮助。本文 Web Workers,Cross-document messaging,WebSockets 的代码在 Firefox 14 下通过測试,Server-Sent Events 的代码在 Chrome 16 下通过測试。

下载

描写叙述 名字 大小 演示样例代码 TestHTML5.zip 13KB

參考资料

学习

关于 HTML5 的规范,请參考 HTML5 关于 HTML5 WebSockets 的规范。请參考 WebSockets。 关于 HTML5 Web Workers 的规范,请參考 Web Workers。

关于 HTML5 Cross-document messaging 的规范,请參考 Cross-document messaging。 关于 HTML5 Server-Sent Events 的规范,请參考 Server-Sent Events。

关于 node.js 的信息,请參考 node.js。

关于 WebSocket-Node 的信息。请參考 WebSocket-Node。 developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程。扩展您在站点开发方面的技能。

developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心。包含非常多文档、教程、论坛、blog、wiki 和新闻。不论什么 Ajax 的新信息都能在这里找到。

developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包含大量 Web 2.0 技术文章、教程、下载和相关技术资源。

查看 HTML5 专题,了解很多其它和 HTML5 相关的知识和动向。

讨论

增加 developerWorks 中文社区。查看开发者推动的博客、论坛、组和维基,并与其它 developerWorks 用户交流。

转载于:https://www.cnblogs.com/bhlsheji/p/5245287.html

相关资源:数据结构—成绩单生成器
最新回复(0)