HTML5之postMessage 和onmessage講解

1 H5 postMessage 和 onmessage

1.1 引言

隨著 HTML5 的發(fā)展于樟,了解并熟悉 HTML5API 接口是非常重要的梯码。postMessage(send) 和 onmessage 此組 APIHTML5 中有著廣泛的應用两疚,比如 Web Workers 中應用此組 API 實現(xiàn)多個線程間 JavaScript 調(diào)用功能 ,Cross-document messaging 中實現(xiàn)兩個不同域間 JavaScript 調(diào)用功能等等惋耙。本文主要介紹此組 APIWeb Workers斯撮,Cross-document messaging台妆,WebSockets 以及 Server-Sent Events 中的詳細應用情況沙廉。

1.2 Web Workers

1.2.1 Web Workers簡介

至 2008 年 W3C 制定出第一個 HTML5 草案開始畴博,HTML5 承載了越來越多嶄新的特性和功能。它不但強化了 Web 系統(tǒng)或網(wǎng)頁的表現(xiàn)性能蓝仲,而且還增加了對本地數(shù)據(jù)庫等 Web 應用功能的支持俱病。其中,最重要的一個便是對多線程的支持袱结。在 HTML5 中提出了工作線程(Web Workers)的概念亮隙,并且規(guī)范出 Web Workers 的三大主要特征:能夠長時間運行(響應),理想的啟動性能以及理想的內(nèi)存消耗垢夹。Web Workers 允許開發(fā)人員編寫能夠長時間運行而不被用戶所中斷的后臺程序溢吻,去執(zhí)行事務或者邏輯,并同時保證頁面對用戶的及時響應果元。
Web WorkersWeb 前端網(wǎng)頁上的腳本提供了一種能在后臺進程中運行的方法促王。一旦它被創(chuàng)建,Web Workers 就可以通過 postMessage 向任務池發(fā)送任務請求而晒,執(zhí)行完之后再通過 postMessage 返回消息給創(chuàng)建者指定的事件處理程序 ( 通過 onmessage 進行捕獲 )蝇狼。Web Workers 進程能夠在不影響用戶界面的情況下處理任務,并且倡怎,它還可以使用 XMLHttpRequest來處理I/O迅耘,但通常,后臺進程(包括 Web Workers 進程)不能對 DOM進行操作监署。如果希望后臺程序處理的結果能夠改變 DOM颤专,只能通過返回消息給創(chuàng)建者的回調(diào)函數(shù)進行處理。
瀏覽器對 HTML5支持情況可以參考網(wǎng)站 https://caniuse.com/

1.2.2 在Web Workers中使用 postMessage 和 onmessage

首先钠乏,需要在客戶端頁面的 JavaScript 代碼中 new 一個 Worker 實例出來栖秕,參數(shù)是需要在另一個線程中運行的 JavaScript文件名稱。然后在這個實例上監(jiān)聽 onmessage 事件晓避。最后另一個線程中的 JavaScript 就可以通過調(diào)用 postMessage方法在這兩個線程間傳遞數(shù)據(jù)了

主線程中創(chuàng)建 Worker 實例簇捍,并監(jiān)聽 onmessage 事件

 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
 <title>Test Web worker</title> 
 <script type="text/JavaScript"> 
         function init(){ 
              var worker = new Worker('compute.js'); 
              //event參數(shù)中有data屬性,就是子線程中返回的結果數(shù)據(jù)
              worker.onmessage= function (event) { 
              // 把子線程返回的結果添加到 div 上
                document.getElementById("result").innerHTML += 
                            event.data+"<br/>"; 
                 }; 
         } 
 </script> 
 </head> 
 <body onload="init()"> 
 <div id="result"></div> 
 </body> 
 </html>

在客戶端的 compute.js 中够滑,只是簡單的重復多次加和操作垦写,最后通過 postMessage 方法把結果返回給主線程,目的就是等待一段時間彰触。而在這段時間內(nèi)梯投,主線程不應該被阻塞,用戶可以通過拖拽瀏覽器,變大縮小瀏覽器窗口等操作測試這一現(xiàn)象分蓖。這個非阻塞主線程的結果就是 Web Workers 想達到的目的尔艇。

compute.js 中調(diào)用 postMessage 方法返回計算結果

var i=0; 

function timedCount(){ 
    for(var j=0,sum=0;j<100;j++){ 
         for(var i=0;i<100000000;i++){ 
            sum+=i; 
         } 
    } 
   // 調(diào)用 postMessage 向主線程發(fā)送消息
    postMessage(sum); 
 } 
 postMessage("Before computing,"+new Date()); 
 timedCount(); 
 postMessage("After computing,"+new Date());

1.3 Cross-document messaging

1.3.1 Cross-document messaging 簡介

由于同源策略的限制,JavaScript 跨域的問題么鹤,一直是一個頗為棘手的問題终娃。HTML5 提供了在網(wǎng)頁文檔之間互相接收與發(fā)送信息的功能。使用這個功能蒸甜,只要獲取到網(wǎng)頁所在窗口對象的實例棠耕,不僅同源(域 + 端口號)的 Web 網(wǎng)頁之間可以互相通信,甚至可以實現(xiàn)跨域通信柠新。 要想接收從其他窗口發(fā)送來的信息窍荧,必須對窗口對象的 onmessage 事件進行監(jiān)聽,其它窗口可以通過 postMessage 方法來傳遞數(shù)據(jù)恨憎。該方法使用兩個參數(shù):第一個參數(shù)為所發(fā)送的消息文本蕊退,但也可以是任何 JavaScript 對象(通過 JSON 轉換對象為文本),第二個參數(shù)為接收消息的對象窗口的 URL 地址憔恳,可以在 URL 地址字符串中使用通配符*指定全部地瓤荔。

1.3.2 在Cross-document messaging中使用 postMessage 和 onmessage

為了實現(xiàn)不同域之間的通信,需要在操作系統(tǒng)的 hosts 文件添加兩個域名钥组,進行模擬输硝。

hosts 文件中添加兩個不同的域名

 127.0.0.1     parent.com 
 127.0.0.1       child.com

在父網(wǎng)頁中通過 iframe 嵌入子頁面,并在 JavaScript代碼中調(diào)用 postMessage 方法發(fā)送數(shù)據(jù)到子窗口者铜。

父頁面中嵌入子頁面腔丧,調(diào)用 postMessage 方法發(fā)送數(shù)據(jù)

 <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 向子窗口發(fā)送數(shù)據(jù)
                 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" onclick="sendIt()" /> 
 </body> 
 </html>

在子窗口中監(jiān)聽 onmessage 事件,并用 JavaScript 實現(xiàn)顯示父窗口發(fā)送過來的數(shù)據(jù)作烟。

子窗口中監(jiān)聽 onmessage 事件,顯示父窗口發(fā)送來的數(shù)據(jù)

 <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 參數(shù)中有 data 屬性砾医,就是父窗口發(fā)送過來的數(shù)據(jù)
         window.addEventListener("message", function( event ) { 
                 // 把父窗口發(fā)送過來的數(shù)據(jù)顯示在子窗口中
           document.getElementById("content").innerHTML+=event.data+"<br/>"; 
         }, false ); 

 </script> 
 </head> 
 <body> 
         Web page from http://child.com:8080 
         <div id="content"></div> 
 </body> 
 </html>

父窗口嵌入子窗口


在這里插入圖片描述

父窗口發(fā)送數(shù)據(jù)到子窗口


在這里插入圖片描述

1.4 WebSockets

1.4.1 WebSockets 簡介

Web 應用中拿撩,HTTP 協(xié)議決定了客戶端和服務端連接是短連接,即客戶端 Request如蚜,服務端 Response压恒,連接斷開。要想實現(xiàn)客戶端和服務端實時通信错邦,只能通過客戶端輪詢來實現(xiàn)探赫。服務端推送數(shù)據(jù)也并不是字面上意思上的直接推,其實還是客戶端自己取撬呢。WebSocketsHTML5 規(guī)范新引入的功能伦吠,用于解決瀏覽器與后臺服務器雙向通訊的問題,使用 WebSockets 技術,后臺可以隨時向前端推送消息毛仪,以保證前后臺狀態(tài)統(tǒng)一搁嗓。

1.4.2 在WebSockets中使用 send 和 onmessage

由于主要介紹 postMessage(send)onmessage 客戶端 API 的應用,而 WebSockets 涉及到服務器端代碼的實現(xiàn)箱靴,所以本文將選取最簡單的服務器端框架來編寫服務器代碼腺逛。WebSockets 服務器端有 jetty 提供的基于 Java 的實現(xiàn),有 WebSocket-Node 基于 node.js 的實現(xiàn)衡怀,在 .Net 4.5 中也直接提供了 WebSockets 的支持棍矛。本文將使用 WebSocket-Node 提供的示例代碼,稍作修改作為 WebSockets 的服務器端

首先抛杨,需要在客戶端通過 JavaScript 代碼 new 一個 WebSocket 實例出來茄靠,參數(shù)是實現(xiàn) WebSocket 服務器端 URL 地址。然后在這個實例上監(jiān)聽 onmessage 事件接收服務器端發(fā)送過來的數(shù)據(jù)蝶桶。當然慨绳,客戶端也可以調(diào)用 send方法,發(fā)送數(shù)據(jù)到服務器端真竖。

創(chuàng)建 WebSocket 對象脐雪,并監(jiān)聽 onmessage 事件

 connect : function() { 
    var location ="ws://localhost:8000/"; 
         // 創(chuàng)建 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 方法中,接收數(shù)據(jù)恢共,并顯示在頁面上

_onmessage 方法

 _onmessage : function(event) { 
         //event 參數(shù)中有 data 屬性战秋,就是服務器發(fā)送過來的數(shù)據(jù)
     if (event.data) { 
         var messageBox = document.getElementById('messageBox'); 
         var spanText = document.createElement('span'); 
         spanText.className ='text'; 
                 // 把服務器發(fā)送過來的數(shù)據(jù)顯示在窗口中
         spanText.innerHTML = event.data; 
         var lineBreak = document.createElement('br'); 
         messageBox.appendChild(spanText); 
         messageBox.appendChild(lineBreak); 
         messageBox.scrollTop = messageBox.scrollHeight 
                 - messageBox.clientHeight; 
     } 
 },

_onopen方法中,調(diào)用 _send 方法發(fā)送一條消息到服務器端讨韭,告之連接已經(jīng)建立脂信。在_onclose 方法中,把 WebSocket 的實例設置成 null透硝,釋放資源狰闪。

_onopen,_onclose 以及 send 方法

 _onopen : function() { 
     server._send("Client:Open WebSockets,"+new Date()); 
 }, 
 //message 參數(shù)就是客戶端向服務器端發(fā)送的數(shù)據(jù)
 _send : function(message) { 
     if (this._ws) 
         this._ws.send(message); 
 }, 
 // 此方法提供外部代碼調(diào)用
 send : function(text) { 
     if (text !=null&& text.length >0) 
         server._send(text); 
 }, 
 
 _onclose : function(m) { 
     this._ws =null; 
 }

把這些方法封裝在一個 server對象中濒生,方便提供外部調(diào)用埋泵。用戶只需要先調(diào)用 serverconnect方法建立連接,然后調(diào)用 send 方法發(fā)送數(shù)據(jù)罪治。

封裝客戶端實現(xiàn)

 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 示例即可

WebSockets 服務器端簡單實現(xiàn)

 // 監(jiān)聽客戶端的連接請求
 wsServer.on('connect', function(connection) { 
    function sendCallback(err) { 
        if (err) console.error("send() error: " + err); 
    } 
         // 監(jiān)聽客戶端發(fā)送數(shù)據(jù)的請求
    connection.on('message', function(message) { 
        if (message.type === 'utf8') {// 區(qū)別客戶端發(fā)過來的數(shù)據(jù)是文本還是二進制類型
            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) { 
    }); 
 });

1.5 Server-Sent Events

1.5.1 Server-Sent Events 簡介

HTML5 Server-Sent 事件模型允許您從服務器 push 實時數(shù)據(jù)到瀏覽器。利用 Eventsource 對象處理與頁面間的接收和發(fā)送數(shù)據(jù)觉义。在客戶端雁社,我們使用 HTML5+JavaScript,服務端使用 Java晒骇。在現(xiàn)存的 Ajax 模式中霉撵,web 頁面會持續(xù)不斷地請求服務器傳輸新數(shù)據(jù)磺浙,由客戶端負責請求數(shù)據(jù)。而在服務端發(fā)送模式下喊巍,無需在客戶端代碼中執(zhí)行連續(xù)的數(shù)據(jù)請求屠缭,而是由服務端 push推送更新。一旦在頁面中初始化了 Server-Sent 事件崭参,服務端腳本將持續(xù)地發(fā)送更新呵曹。客戶端 JavaScript代碼一旦接收到更新就將新的數(shù)據(jù)寫入頁面中展示出來何暮。

1.5.2 在 Server-Sent Events 中使用 onmessage

Server-Sent EventsWebSockets 有相同之處奄喂,WebSockets 實現(xiàn)了服務器端以及客戶端的雙向通信功能,而 Server-Sent Events 則僅是指服務器端到客戶端的單向通信海洼,而且 Server-Sent Events 同樣需要服務器端的實現(xiàn)跨新,本文將使用基于 JavaServlet 技術實現(xiàn)服務器端
首先,在客戶端通過 JavaScript 代碼 new 一個 EventSource實例出來坏逢,參數(shù)是實現(xiàn) EventSource 服務器端URL 地址域帐。然后在這個實例上監(jiān)聽 onmessage事件接收服務器端發(fā)送過來的數(shù)據(jù)。

創(chuàng)建 EventSource 對象是整,并監(jiān)聽 onmessage 事件

 if (!!window.EventSource) { 
 // 創(chuàng)建 EventSource 實例肖揣,傳入 server 地址
          var source = new EventSource('/TestHTML5/ServerSentEvent'); 
         } else { 
          console.log("Your browser doesn't support server-sent event"); 
 } 
 // 監(jiān)聽 message 事件,等待接收服務器端發(fā)送過來的數(shù)據(jù)
 source.addEventListener('message', function(event) { 
                 //event 參數(shù)中有 data 屬性浮入,就是服務器發(fā)送過來的數(shù)據(jù)
          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);

服務器端龙优,在 Java 語言實現(xiàn)的 Servlet doGet 方法中使用 response 對象向客戶端寫數(shù)據(jù)

服務器端簡單實現(xiàn)

 // 這里必須設置 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); 
 // 向客戶端寫兩行數(shù)據(jù)
 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();

點擊了解JavaScript中postMessage

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市事秀,隨后出現(xiàn)的幾起案子彤断,更是在濱河造成了極大的恐慌,老刑警劉巖易迹,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宰衙,死亡現(xiàn)場離奇詭異,居然都是意外死亡赴蝇,警方通過查閱死者的電腦和手機菩浙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來句伶,“玉大人,你說我怎么就攤上這事陆淀】加啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵轧苫,是天一觀的道長楚堤。 經(jīng)常有香客問我疫蔓,道長,這世上最難降的妖魔是什么身冬? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任衅胀,我火速辦了婚禮,結果婚禮上酥筝,老公的妹妹穿的比我還像新娘滚躯。我一直安慰自己,他們只是感情好嘿歌,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布掸掏。 她就那樣靜靜地躺著,像睡著了一般宙帝。 火紅的嫁衣襯著肌膚如雪丧凤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天步脓,我揣著相機與錄音愿待,去河邊找鬼。 笑死靴患,一個胖子當著我的面吹牛仍侥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚁廓,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼访圃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了相嵌?” 一聲冷哼從身側響起腿时,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎饭宾,沒想到半個月后批糟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡看铆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年徽鼎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弹惦。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡否淤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棠隐,到底是詐尸還是另有隱情石抡,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布助泽,位于F島的核電站啰扛,受9級特大地震影響嚎京,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隐解,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一鞍帝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧煞茫,春花似錦帕涌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炸宵,卻和暖如春辟躏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背土全。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工捎琐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裹匙。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓瑞凑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親概页。 傳聞我的和親對象是個殘疾皇子籽御,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • HTML5應用程序緩存 使用 HTML5,通過創(chuàng)建 cache manifest 文件惰匙,可以輕松地創(chuàng)建 web 應...
    羅_c857閱讀 281評論 0 0
  • 接上 關于HTML/HTML5(一)http://www.reibang.com/p/33fc7840c079 學...
    Amyyy_閱讀 791評論 0 4
  • HTML5 是 HTML 最新的修訂版本技掏,2014 年 10 月由萬維網(wǎng)聯(lián)盟(W3C)完成標準制定。 HTML5 ...
    神齊閱讀 964評論 0 1
  • 一项鬼、HTML5簡介 HTML5是根據(jù)2014年由萬維網(wǎng)聯(lián)盟(W3C)完成標準制定的哑梳,他的設計目的主要是為了在移動設...
    劉松陽閱讀 304評論 0 0
  • http://www.w3schools.com/html/html5_intro.aspHTML5的瀏覽器支持:...
    JenniferYe閱讀 811評論 0 1