前言: 本篇文章我將帶你們一塊兒來好好認識一下postMessage,包括它的兼容性,對應的API介紹,以及常見的幾個使用場景,但願能夠給有一樣困惑的盆友們一點啓發,給須要用這個技術的同僚們一些幫助.html
postMessage是html5引入的API,postMessage()方法容許來自不一樣源的腳本採用異步方式進行有效的通訊,能夠實現跨文本文檔,多窗口,跨域消息傳遞.多用於窗口間數據通訊,這也使它成爲跨域通訊的一種有效的解決方案.vue
下圖是在caniuse上面搜到的postMessage兼容性截圖,除IE瀏覽器的支持度比較低外,,其餘瀏覽器的支持度良好.html5
otherWindow.postMessage(message, targetOrigin, [transfer]);
複製代碼
窗口的一個引用,好比iframe的contentWindow屬性,執行window.open返回的窗口對象,或者是命名過的或數值索引的window.frames.node
要發送到其餘窗口的數據,它將會被[!結構化克隆算法](https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm)序列化.這意味着你能夠不受什麼限制的將數據對象安全的傳送給目標窗口而無需本身序列化.jquery
經過窗口的origin屬性來指定哪些窗口能接收到消息事件,指定後只有對應origin下的窗口才能夠接收到消息,設置爲通配符"*"表示能夠發送到任何窗口,但一般處於安全性考慮不建議這麼作.若是想要發送到與當前窗口同源的窗口,可設置爲"/"nginx
是一串和message同時傳遞的**Transferable**對象,這些對象的全部權將被轉移給消息的接收方,而發送一方將再也不保有全部權.git
window.addEventListener("message", receiveMessage, false) ;
function receiveMessage(event) {
var origin= event.origin;
console.log(event);
}複製代碼
event對象的打印結果截圖以下:web
這裏重點介紹event對象的四個屬性ajax
咱們都知道JSONP能夠實現解決GET請求的跨域問題,可是不能解決POST請求的跨域問題.而postMessage均可以.這裏只是列舉一個示例,僅供參考,具體的代碼如何編寫要以具體的場景而定奧~
父窗體建立跨域iframe併發送信息算法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>跨域POST消息發送</title>
<script type="text/JavaScript">
// sendPost 經過postMessage實現跨域通訊將表單信息發送到 moweide.gitcafe.io上,
// 並取得返回的數據
function sendPost() {
// 獲取id爲otherPage的iframe窗口對象
var iframeWin = document.getElementById("otherPage").contentWindow;
// 向該窗口發送消息
iframeWin.postMessage(document.getElementById("message").value,
'http://moweide.gitcafe.io');
}
// 監聽跨域請求的返回
window.addEventListener("message", function(event) {
console.log(event, event.data);
}, false);
</script>
</head>
<body>
<textarea id="message"></textarea>
<input type="button" value="發送" onclick="sendPost()">
<iframe
src="http://moweide.gitcafe.io/other-domain.html" id="otherPage"
style="display:none"></iframe>
</body>
</html>複製代碼
子窗體接收信息並處理
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>POST Handler</title>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/JavaScript">
window.addEventListener("message", function( event ) {
// 監聽父窗口發送過來的數據向服務器發送post請求
var data = event.data;
$.ajax({
// 注意這裏的url只是一個示例.實際練習的時候你須要本身想辦法提供一個後臺接口
type: 'POST',
url: 'http://moweide.gitcafe.io/getData',
data: "info=" + data,
dataType: "json"
}).done(function(res){
//將請求成功返回的數據經過postMessage發送給父窗口
window.parent.postMessage(res, "*");
}).fail(function(res){
//將請求失敗返回的數據經過postMessage發送給父窗口
window.parent.postMessage(res, "*");
});
}, false);
</script>
</head>
<body></body>
</html>複製代碼
JavaScript語言採用的是單線程模型,一般來講,全部任務都在一個線程上完成,一次只能作一件事,後面的任務要等到前面的任務被執行完成後才能夠開始執行,可是這種方法若是遇到複雜費時的計算,就會致使發生阻塞,嚴重阻礙應用程序的正常運行.Web Worker爲web內容在後臺線程中運行腳本提供了一種簡單的方法,線程能夠執行任務而不干擾用戶界面.一旦建立,一個worker能夠將消息發送到建立它的JavaScript代碼,經過消息發佈到改代碼指定的事件處理程序.
一個woker是使用一個構造函數建立一個對象,運行一個命名的JavaScript文件-這個文件將包含在工做線程中運行的代碼,woker運行在另外一個全局上下文中,不一樣於當前的window,不能使用window來獲取全局屬性.
一些侷限性
alert()
方法和confirm()
方法,但可使用 XMLHttpRequest 對象發出 AJAX 請求workers和主線程間的數據傳遞經過這樣的消息機制進行——雙方都使用postMessage()方法發送各自的消息,使用onmessage事件處理函數來響應消息(消息被包含在Message
事件的data屬性中)。這個過程當中數據並非被共享而是被複制;woker分爲專用worker和共享worker,一個專用worker緊急能被首次生成它的腳本使用,而共享woker能夠同時被多個腳本使用.
專用woker使用示例:
// main.js
if(window.Worker) {
var myWorker = new Worker('http://xxx.com/worker.js');
// 發送消息
first.onchange = function() {
myWorker.postMessage([first.value, second.value]);
console.log("Message posted to worker");
}
second.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
// 主線程 監聽onmessage以響應worker回傳的消息
myWorker.onmessage = function (e) {
var textContent = e.data;
console.log("message received from worker");
}
}
// worker.js
// 內置selfduixiang,,表明子線程自己, worker內部要加載其餘腳本,可經過importScripts()方法
onmessage = function(e) {
console.log("message received from main script");
var workerResult = "Result: " + (e.data[0] * e.data[1]);
console.log("posting message\back to main script");
postMessage(workerResult);
}複製代碼
Web Worker的使用場景,用於收集埋點數據,能夠用於大量複雜的數據計算,複雜的圖像處理,大數據的處理.由於它不會阻礙主線程的正常執行和頁面UI的渲染.
埋點數據採集下的使用: 可在main.js中收集數據,將收集到的信息經過postMessage的方式發送給worker.js,在woker.js中進行相關運算和整理併發送到服務器端;固然,不使用Web Woker,經過在單頁面應用中的index.html中建立iframe也能夠實現頁面間切換,頁面停留時長等數據的採集,具體的實現我就不細講了,感興趣的同窗可在網上搜索解決方案,有什麼疑問歡迎私信我~~~
可在瀏覽器控制檯的application中裏看到Service Worker的存在
Service Worker是web應用作離線存儲的一個最佳的解決方案,Service Worker和Web Worker的相同點是在常規的js引擎線程之外開闢了新的js線程去處理一些不適合在主線程上處理的業務,不一樣點主要包括如下幾點:
咱們可使用Service Worker來進行緩存,用js來攔截瀏覽器的http請求,並設置緩存的文件,從而建立離線web應用.關於Service Worker的概念的介紹就到這裏~~,感興趣的能夠找相關文章學習,有疑問的歡迎私信與我探討~,這裏咱們主要介紹的是使用postMessage方法進行Service Worker和頁面之間的通信.
從頁面發送信息到Service Worker
須要注意一點,這個頁面若是直接扔進瀏覽器裏(使用的是file協議)打開是會報錯的,要使用nginx作端口映射,或者用node搭建一個服務器(使用http協議)來訪問該頁面(目前我猜想的緣由是瀏覽器對file協議打開的文件作了一些服務的限制,若是有大佬知道具體緣由還望告知).下文也將附上個人nginx作端口映射的配置.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Service Worker跨窗口通訊</title>
</head>
<body>
<textarea id="showArea"></textarea>
<script src="sw1.js"></script>
<script src="sw2.js"></script>
<script type="text/JavaScript">
if('serviceWorker' in window.navigator) {
// 對於多個不一樣scope的多個Service Worker,咱們也能夠給指定的Service Worker發送消息
navigator.serviceWorker.register('./sw1.js', { scope:'./sw1'})
.then(function(reg) {
console.log('success', reg);
return new Promise((resolve, reject) => {
const interval = setInterval(function() {
if(reg.active) {
clearInterval(interval);
resolve(reg.active);
}
}, 1000);
}).then(sw => {
sw.postMessage("this message is from page to sw1");
})
})
navigator.serviceWorker.register('./sw2.js', { scope:'./sw2'})
.then(function(reg) {
console.log('success', reg);
return new Promise((resolve, reject) => {
const interval = setInterval(function() {
if(reg.active) {
clearInterval(interval);
resolve(reg.active);
}
}, 1000);
}).then(sw => {
sw.postMessage("this message is from page to sw2");
})
});
navigator.serviceWorker.addEventListener('message', function (event) {
console.log(event.data);
// 接受數據,並填充在 DOM 中
document.getElementById('showArea').value = event.data ;
});
}
</script>
</body>
</html>
// sw1.js
self.addEventListener("message", function(event) {
console.log("sw1.js " + event.data);
event.source.postMessage('this message is from sw1.js, to page');
});
// sw2.js
self.addEventListener("message", function(event) {
console.log("sw2.js " + event.data);
// event.source是消息來源頁面對象的引用
event.source.postMessage('this message is from sw2.js, to page');
});複製代碼
nginx作端口映射的相關配置:
// nginx.conf
// 由於有多個項目會用到nginx服務作端口映射
// 因此我在nginx的目錄下新建了conf.d的文件來存放每一個項目的配置.
// 而後在主配置文件裏經過include引入
http {
# 這裏省略了一些你本機電腦上的nginx服務的配置
include conf.d/*.conf;
}
// testHtml.conf
server {
listen 9090;
server_name localhost;
location / {
root C:/Users/hzljie/Desktop/test/testb;
# 這是個人測試頁面的存放路徑,讀者用的時候記得根據本身的來更改奧
index test.html test.htm;
}
}
複製代碼
運行的效果的截圖:
這樣就實現了Service Worker 與其餘頁面的通訊,感興趣的小夥伴能夠一塊兒來試一試奧.若是你出現報錯的狀況,要仔細檢查本身的代碼奧~
嗯,花了一番功夫終於總結完了,可是文章可能不夠詳盡,畢竟一個技術會有不少擴展和分支領域,很難一一介紹清楚,我寫博客的水平也還有待提高,歡迎對這個有研究或者有興趣或者發現文章有錯誤的地方的夥伴們和我交流,共同進步~~~.個人郵箱2510909248@qq.com.