[譯]如何在Service Worker和網頁客戶端之間發送消息

原文javascript

Service Workers是一個爲頁面工做的後臺處理器。提供離線web apps是Service Workers目前最讓人感興趣的功能,同時Service Workers可以管理一個本地的資源緩存,當網絡鏈接狀態是正常的時候,這個本地資源緩存可以自動跟服務器進行同步。這是十分酷的,但我想談一下Service Workers的另外一個用途,使用它來管理多個web頁面之間的通訊。html

例如,你可能有一個應用打開在多個瀏覽器頁籤中。Service Workers可以更新一個頁籤當其餘頁簽有一個事件觸發,也能夠作到當服務器發出一個消息後,全部頁籤的內容將被更新。java

一個Service Workers能夠控制多個客戶端頁面,例如ServiceWorker會自動的控制它範圍內的全部客戶端頁面,它的範圍是指你站點下的url,一般來說是Service Worker script文件的路徑。web

在這個demo裏,咱們將使用三個文件 client1.html client2.html service-worker.js瀏覽器

首先咱們註冊service worker 在 client1.html緩存

<!doctype html>
<html>
<head>
    <title>Service Worker - Client 1</title>
</head>
<body>
    <script> if('serviceWorker' in navigator){ // Register service worker navigator.serviceWorker.register('/service-worker.js').then(function(reg){ console.log("SW registration succeeded. Scope is "+reg.scope); }).catch(function(err){ console.error("SW registration failed with error "+err); }); } </script>
</body>
</html>
複製代碼

接着咱們建立一個基本的 Service worker 在service-worker.js服務器

console.log("SW Startup!");

// Install Service Worker
self.addEventListener('install', function(event){
    console.log('installed!');
});

// Service Worker Active
self.addEventListener('activate', function(event){
    console.log('activated!');
});
複製代碼

我不會去解釋他是怎麼工做的,由於在不少地方都有記錄(譯者注:做者的意思是不少地方都有console.log)網絡

咱們同時建立了client2.html 咱們只會在client1註冊serviceworker,因此不須要有重複的代碼在這裏。serviceworker運行的時候將會自動的控制它的做用域下的頁面。app

<!doctype html>
<html>
<head>
    <title>Service Worker - Client 2</title>
</head>
<body>
    <script> </script>
</body>
</html>
複製代碼

若是你在瀏覽器上訪問client1.html你應該會看到由console.log輸出的註冊信息。在Chrome(48+)中,你能夠在開發工具的「Resouces」選項卡下點擊「inspect」,爲服務工做者打開一個檢查器。(譯者注:這個我沒有找到)。當你打開client2.html在新的瀏覽器頁籤,你能夠在開發工具內的「Controlled Clients」找到它函數

如今咱們能夠繼續講有趣的東西了。


首先咱們讓客戶端發消息給serviceworker。全部咱們須要去加一個消息的處理在service-worker.js

self.addEventListener('message', function(event){
    console.log("SW Received Message: " + event.data);
});
複製代碼

如今咱們增長一個發消息的函數在兩個客戶端裏

function send_message_to_sw(msg){
    navigator.serviceWorker.controller.postMessage("Client 1 says '"+msg+"'");
}
複製代碼

若是你在客戶端頁面的控制檯內調用send_message_to_sw("Hello"),你因該能夠在serviceworker的控制檯內看到有消息顯示

咱們能夠進一步的讓serviceworker去響應客戶端發來的消息。實現它咱們須要去改良咱們的send_message_to_sw函數。咱們使用‘Message Channel’,Message Channel可以提供了一對端口(port)來進行通訊。咱們將一個引用連同消息一塊兒發送到端口的另外一端,因此Service Worker可以使用它去進行答覆。咱們也能夠對這些響應消息作一些處理。爲了方便起見,咱們還使用Promise來處理等待響應。

譯者注:這裏說的端口(port)用於頁面與serviceworker之間的通訊

function send_message_to_sw(msg){
    return new Promise(function(resolve, reject){
        // Create a Message Channel
        var msg_chan = new MessageChannel();

        // Handler for recieving message reply from service worker
        msg_chan.port1.onmessage = function(event){
            if(event.data.error){
                reject(event.data.error);
            }else{
                resolve(event.data);
            }
        };

        // Send message to service worker along with port for reply
        navigator.serviceWorker.controller.postMessage("Client 1 says '"+msg+"'", [msg_chan.port2]);
    });
}
複製代碼

在*service-worker.js *咱們修改了監聽器,與消息一塊兒發送響應在端口

self.addEventListener('message', function(event){
    console.log("SW Received Message: " + event.data);
    event.ports[0].postMessage("SW Says 'Hello back!'");
});
複製代碼

如今若是在你的客戶端控制檯執行send_message_to_sw("Hello").then(m => console.log(m)),你將看到信息顯示在serviceworker的控制檯裏,在客戶端的控制檯將會有答覆。請注意,咱們使用Promise then函數來等待響應和箭頭函數,由於這樣更容易去測定(譯者注:這裏type我翻譯成控制)。

如今咱們有了一個讓客戶端發消息給serviceworker同時serviceworker可以答覆的機制。您能夠使用它讓客戶機檢查長時間運行的流程的狀態,讓serviceworker將消息轉發給全部客戶端或其餘一些很酷的東西。


如今咱們容許serviceworker廣播一個事件到全部的客戶端讓全部客戶響應。這與之前使用的機制相似,只是角色顛倒了。

首先咱們在客戶端增長一個消息監聽器,咱們增長了測試serviceworker兼容性的代碼,其餘的地方几乎相同。

if('serviceWorker' in navigator){
    // Handler for messages coming from the service worker
    navigator.serviceWorker.addEventListener('message', function(event){
        console.log("Client 1 Received Message: " + event.data);
        event.ports[0].postMessage("Client 1 Says 'Hello back!'");
    });
}
複製代碼

接着咱們給serviceworker增長一個發送消息給客戶端的函數。這也跟以前很相似,只是咱們須要提供給一個客戶端對象(一個頁面的應用),這個對象能告訴咱們要往哪裏發消息。

function send_message_to_client(client, msg){
    return new Promise(function(resolve, reject){
        var msg_chan = new MessageChannel();

        msg_chan.port1.onmessage = function(event){
            if(event.data.error){
                reject(event.data.error);
            }else{
                resolve(event.data);
            }
        };

        client.postMessage("SW Says: '"+msg+"'", [msg_chan.port2]);
    });
}
複製代碼

serviceworker API提供了獲取全部已鏈接客戶端引用的接口。咱們能夠將其封裝在一個方便的函數中,以便向全部客戶機廣播消息(注意,咱們再次使用箭頭函數)。

function send_message_to_all_clients(msg){
    clients.matchAll().then(clients => {
        clients.forEach(client => {
            send_message_to_client(client, msg).then(m => console.log("SW Received Message: "+m));
        })
    })
}
複製代碼

如今若是咱們在serviceworker的控制檯內執行send_message_to_all_clients('Hello'),您將看到在全部客戶端控制檯中接收到的消息,以及在serviceworker控制檯中客戶機的響應。

相關文章
相關標籤/搜索