Web Worker && postMessage && onMessage 使用教程

**轉(zhuǎn)自博客原文連接:https://tong-h.github.io/2019/04/21/webworker/
**

最近使用 iframe 的時候想要獲取 iframe 文檔信息的時候遇到了跨域問題,最后使用 postmessage 做父子頁面通信解決需求也順便學(xué)習(xí)了下 webworker 的使用
webWoker 使用依賴 postMessage() 和 onMessage(), 所以先說這兩個吧

postMessage && onMessage

  • 提供網(wǎng)頁文檔之間互相發(fā)送和接收信息的功能,可用于解決跨域訪問的問題
  • 會在所有頁面腳本執(zhí)行完畢之后(包括方法之后或者之前設(shè)置的timeout 事件)再執(zhí)行
  • 掛載于window對象上

postMessage 語法: otherWindow.postMessage(message, targetOrigin, [transfer]);

??message: 發(fā)送的數(shù)據(jù),不限類型,因為他自己會序列化
??targetOrigin:通過窗口的origin屬性指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI
??transfer:可選參數(shù);一個 Transferable 對象([什么是Transferable[(https://developer.mozilla.org/zh-CN/docs/Web/API/Transferable)),和message 同時傳遞的,對象的所有權(quán)將被轉(zhuǎn)移給消息的接收方,而發(fā)送一方將不再保有所有權(quán)

onMessage 語法:

window.addEventListener('message', function(event) { ... })
window.onmessage = function(event) { ... }

獲取到的 event 對象包含

??data:接收的數(shù)據(jù)對象,對應(yīng) postMessage 的 message 參數(shù)
??origin:消息發(fā)送方窗口的 origin,字符串由 協(xié)議、“://“、域名、“ : 端口號”拼接而成
??source:對發(fā)送消息的窗口對象的引用

舉個小栗子

  • 父頁面向子頁面?zhèn)髦?/li>
// 在 iframe 加載完畢后,獲取 iframe 的 window 對象,調(diào)用 postMessage 方法
<body>
    <div>a page</div>
    <iframe src="b.html" id="frame"></iframe>
    <script>
        var frame = document.getElementById('frame')
        frame.onload = function(){
            frame.contentWindow.postMessage({ name: 'a page' },'*');
        }
    </script>
</body>

// 子頁面監(jiān)聽 message 事件設(shè)置回調(diào)打印 event
<body>
    <div>b page</div>
    <script>
        window.addEventListener("message", (event) => console.log('this b page', event), false);
    </script>
</body>
  • 子頁面向父頁面?zhèn)髦?/li>
// 父頁面監(jiān)聽 message 事件設(shè)置回調(diào)打印 event
<body>
    <div>a page</div>
    <iframe src="b.html" id="frame"></iframe>
    <script>
        window.addEventListener("message", (event) => console.log('this a page', event), false);
    </script>
</body>

// 使用 parent 獲取 window 對象,調(diào)用 postMessage 方法
<body>
    <div>b page</div>
    <script>
        console.log(parent)
        parent.postMessage( {name: 'b page'}, '*');
    </script>
</body>

Workers

  • 我自己感覺很多比較麻煩耗內(nèi)存的js邏輯操作都可以放在worker里,比如輪詢服務(wù)器狀態(tài)或者一些很耗時量很大的數(shù)據(jù)操作用
  • 讓腳本在瀏覽器后臺線程中運行
  • 在worker內(nèi),不能直接操作DOM節(jié)點,也不能使用window對象的默認(rèn)方法和屬性,window對象下可用的方法
  • worker 中也能再創(chuàng)建 worker
  • 由于安全限制 Worker 不能讀取本地文件,所以腳本必須來自網(wǎng)絡(luò),讀取本地文件會報錯 "Uncaught SecurityError: Failed to create a worker: script at '(path)/worker.js' cannot be accessed from origin 'null'."
  • 關(guān)于兼容性

用法

在使用 worker 的 js文件里

// 使用 Worker() 指定腳本 url 創(chuàng)建一個新的 worker
// 參數(shù)就是 Worker 線程所要執(zhí)行的任務(wù)
// Worker 使用 postMessage 和 onMessage 進行通信
var myWorker = new Worker("worker.js");

// 使用 postMessage() 和 onMessage() 發(fā)送和接收數(shù)據(jù)
myWorker.postMessage("request");

在 worker.js文件

// 消息響應(yīng)
onmessage = function(e) {
  console.log(e.data);
  console.log(self)
  // 消息回傳
  postMessage(workerResult);
}

關(guān)閉/錯誤/加載腳本

// 在 main.js 文件中,強制終止
worker.terminate();

// 在 worker 線程中,自己關(guān)閉
self.close();

// error 錯誤代理
worker.onerror(function (event) {});

// 發(fā)送的數(shù)據(jù)無法序列化成字符串時,會觸發(fā)這個事件
Worker.onmessageerror(function (event) {});

// worker 使用 importScripts() 加載腳本,可以加載多個
importScripts('script1.js', 'script2.js');

小栗子

如果你現(xiàn)在沒有條件加載網(wǎng)絡(luò)上的文件,可以使用 URL.createObjectURL 方法建立緩存 URL
可以試著運行一下面兩個頁面感受一下

可以運行一下這個頁面,一個普通的 for 循環(huán),因為數(shù)字太大運行時會有明顯的卡頓

<html> 
<head> 
<title>Test Web worker</title> 
<script type="text/JavaScript">
    window.onload = function(){
        for(var num=99;num<1000000000;num++){ 
            document.getElementById("numshow").innerHTML += event.data+"<br/>"; 
        }
    }
</script>
</head> 
<body id="numshow">
</body>
</html>

這個可以使用 URL.createObjectURL 方法建立緩存 URL

<html> 
<head> 
<title>Test Web worker</title> 
<script type="text/JavaScript">

    window.onload = function() {

        console.log(5);
        
        var worker = new Worker(URL.createObjectURL(new Blob(["(" + webWorker.toString() + ")()"], {type: 'text/javascript'})));
        
        worker.onmessage= (event) => { 
            
            // 數(shù)據(jù)打印
            console.log(event.data);
            document.getElementById("numshow").innerHTML += event.data+"<br/>"; 

            // 向 worker 線程發(fā)送數(shù)據(jù)
            event.data === 1 ? worker.postMessage('num'):''
        }; 
    }

    function webWorker() {

        // worker 對象 self
        console.log(self)

        // 接收來自主線程的數(shù)據(jù)
        self.onmessage = function (event) { 
            console.log(event.data);
        }

        for (var num = 1; num < 1000000000; num++) { 

            // 當(dāng) num === 200的時候關(guān)閉 worker 線程
            num === 200 ? (postMessage("worker關(guān)閉"), close()) : postMessage(num**2)
        }
    }
    
</script>
</head> 
<body id="numshow">
</body>
</html>

參考文章

postMessage:MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
webworker:MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
阮一峰:http://www.ruanyifeng.com/blog/2018/07/web-worker.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容