問題場景:
web是嵌入到手機客戶端中的靜態頁面,為了統計用戶行為需要引入ga,但是ga必須是在www下才行,哪怕是localhost,這就是矛盾。解決方案是在頁面中使用iframe,iframe是在另外一個域名下的,然后在iframe中調用ga方法。很顯然必須要解決iframe的跨域通信。
var frame=document.getElementById("gaFrame"); document.body.onclick=function(){ var sendData={ "name":"deals_list", "event":"deals_click", "e_data":"這是發送的數據" } frame.contentWindow.postMessage(sendData,"*") }
每次點擊指定區域時候向iframe中發送消息,然后在iframe中監聽消息數據,發送ga。
var OnMessage = function(e) { var data=e.data; ga('send', 'pageview',"/"+data); } function init() { if (window.addEventListener) { // all browsers except IE before version 9 window.addEventListener("message", OnMessage, false); } else { if (window.attachEvent) { // IE before version 9 window.attachEvent("onmessage", OnMessage); } } }; init();
好,實際場景和方法介紹完了,開工學習相關知識。
window.postMessage
window.postMessage
是一個用於安全的使用跨源通信的方法。通常,不同頁面上的腳本只在這種情況下被允許互相訪問,當且僅當執行它們的頁面所處的位置使用相同的協議(通常都是 http)、相同的端口(http默認使用80端口)和相同的主機(兩個頁面的 document.domain 的值相同)。 在正確使用的情況下,window.postMessage
提供了一個受控的機制來安全地繞過這一限制。
window.postMessage
, 調用時,掛起的腳本必須執行完成才會將 MessageEvent 派遣至目標window (例如:如果一個事件處理程序調用了window.postMessage,剩余的事件處理程序就會掛起超時等). MessageEvent 有消息類型, 它被設置為第一個參數值提供給window.postMessage的data屬性,
對應的window調用window.postMessage的時候,window.postMessage主文檔的來源的origin屬性被稱為源屬性,指哪個調用window.postMessage的窗口。 (事件的其他標准屬性都存在與對應的預期值.)
語法:
otherWindow.postMessage(message, targetOrigin);
otherWindow
引用另外一個窗口,比如上面實例中的iframe,contentWindow
是iframe中的window對象。
message
發送到其他window中的數據
targetOrigin
目標源,可以限定只接收來自某個URL下的數據
監聽發送事件
window.addEventListener("message", receiveMessage, false); function receiveMessage(event) { if (event.origin !== "http://example.org:8080") return; // ... }
data:來自其他window的數據。
origin:調用postMessage的窗口url。
source:發送消息窗口的引用。可以使用該方法使來自不同源的窗口進行雙向通信。
安全性
var popup = window.open(...popup details...); // When the popup has fully loaded, if not blocked by a popup blocker: // This does nothing, assuming the window hasn't changed its location. popup.postMessage("The user is 'bob' and the password is 'secret'", "https://secure.example.net"); // This will successfully queue a message to be sent to the popup, assuming // the window hasn't changed its location. popup.postMessage("hello there!", "http://example.org"); function receiveMessage(event) { // Do we trust the sender of this message? (might be // different from what we originally opened, for example). if (event.origin !== "http://example.org") return; // event.source is popup // event.data is "hi there yourself! the secret response is: rheeeeet!" } window.addEventListener("message", receiveMessage, false);
/* * In the popup's scripts, running on <http://example.org>: */ // Called sometime after postMessage is called function receiveMessage(event) { // Do we trust the sender of this message? if (event.origin !== "http://example.com:8080") return; // event.source is window.opener // event.data is "hello there!" // Assuming you've verified the origin of the received message (which // you must do in any case), a convenient idiom for replying to a // message is to call postMessage on event.source and provide // event.origin as the targetOrigin. event.source.postMessage("hi there yourself! the secret response " + "is: rheeeeet!", event.origin); } window.addEventListener("message", receiveMessage, false);
在web worker是中使用postMessage和onMessage
主線程中創建 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 onload="init()"> <div id="result"></div> </body> </html>
在客戶端的 compute.js 中,只是簡單的重復多次加和操作,最后通過 postMessage 方法把結果返回給主線程,目的就是等待一段時間。而在這段時間內,主線程不應該被阻塞,用戶可以通過拖拽瀏覽器,變大縮小瀏覽器窗口等操作測試這一現象。這個非阻塞主線程的結果就是 Web Workers 想達到的目的。
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());