[译]如何在Service Worker和网页客户端之间发送消息

原文html

Service Workers是一个为页面工做的后台处理器。提供离线web apps是Service Workers目前最让人感兴趣的功能,同时Service Workers可以管理一个本地的资源缓存,当网络链接状态是正常的时候,这个本地资源缓存可以自动跟服务器进行同步。这是十分酷的,但我想谈一下Service Workers的另外一个用途,使用它来管理多个web页面之间的通讯。web

例如,你可能有一个应用打开在多个浏览器页签中。Service Workers可以更新一个页签当其余页签有一个事件触发,也能够作到当服务器发出一个消息后,全部页签的内容将被更新。浏览器

一个Service Workers能够控制多个客户端页面,例如ServiceWorker会自动的控制它范围内的全部客户端页面,它的范围是指你站点下的url,一般来说是Service Worker script文件的路径。缓存

在这个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.jsapp

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运行的时候将会自动的控制它的做用域下的页面。工具

<!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”找到它post

如今咱们能够继续讲有趣的东西了。


首先咱们让客户端发消息给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控制台中客户机的响应。

相关文章
相关标签/搜索