相互传送信息
撰写时间:2025-01-02
修订时间:2025-01-02
另一种注册方式
在本页面中,我们使用另一种注册方式。重点考查对象为我们最早遇到的navigator.serviceWorker,其类型为ServiceWorkerContainer。
因为要多次使用navigator.serviceWorker,因此先将其值赋于变量container。在doReg方法中,先调用container的getRegistration方法来检查之前是否已经注册。如果没有,调用其register方法来注册。
container的ready方法返回一个成功注入了ServiceWorkerRegistration的Promise。因此doReg返回经解析后的ServiceWorkerRegistration的一个实例,最重要的是,该实例中的active worker已被成功设置,而此时,container的controller属性指向了这个当前正提供服务的Service Worker。
因此,尽管一个成功注册的ServiceWorkerRegistration有多个不同状态的Service Workers,且它们会在不同的场合下自动进行轮候转换的复杂操作,但在调用container的ready方法后,则可通过其controller属性安全地获取当前正在提供服务的Service Worker。
因为controller是全局唯一性的,在访问不同的服务器路径、使用不同的ServiceWorkerRegistration时,都会导致controller自动切换指向不同的Service Worker,因此,我们可以将其视为当前作用域下正在提供服务的Service Worker
。
第一次访问本页面时,controller可能指向了其他路径下的Service Worker,因此代码:
的值为false。而第二次刷新本页面,则该值转变为true,就是这个原因。
封装进自定义的类
明白了上述细节,我们可以封装进一个自定义的类ServiceWorkerUtils中。
短短几行代码,GetActiveWorker进行了检查、注册、等待状态转换并最终返回一个注入了激活的Service Worker的Promise的一系列动作。
之后,像下面这样进行调用:
这样,我们在页面端的主线程中就持有了一个当前正在提供服务的activeWorker。
activeWorker有一个postMessage方法,下面我们将调用它向Service Worker发送信息。
主线程向Service Worker发送信息
主线程向激活的Service Worker发送信息:
postMessage的参数可为任意对象而不仅仅是文本,这里,我们将要发送的信息包装为一个对象,type属性表示消息的类型,msg表示要传送的消息文本。
在caching.js文件中,其处理来自主线程所发来信息的代码如下:
如果msgWrapper的type属性值为normal
,则直接在终端中输出信息。上面的...
为其他代码部分,但与这里的逻辑无关,因此这里暂不显示。
在Chrome的终端中,您将看到其输出:
表明Service Worker收到了主线程所发过来的信息。
但问题在于,Service Worker无法直接向主线程反向发送信息。
但这难不倒我们。
实现双向通讯
我们可以通过MessageChannel来实现双向通讯。
MessageChannel是HTML 5规范中所定义的一个类,以通道的方式,在不同环境中实现了一种较为安全的、且可明确授权的双向通讯机制。
在使用上,MessageChannel非常简单。该类实例只有2个属性:port1及port2,表示通道的两个通讯端口。编写代码时,只需说明哪一方使用哪个端口就行了。代码:
在向activeWorker发送信息时,将port2端口一并传送,这就指定了activeWorker应使用port2端口来发送信息。
而port1端口则由网页端的主线程用以接收通道信息:
在主线程接收到Service Worker所返回的信息时,从上面的代码可以获知,参数evt的类型为MessageEvent,其data属性即传递的对象。其ports是一个数组。从上面代码运行后输出结果来看,这是一个空数组。
这里这个代表端口数据的数组为何为空数组?因为在主线程中,我们已经可以直接向activeWoker发送信息,因此activeWoker返回信息时无需再将多余的端口一并传送过来。这里打印该属性的目的是,网页端主线程的evt与activeWorker端的evt是同一类型。但由于Service Worker不能访问DOM,因此不方便展示其类型。所以在这里通过本站的PageConsole先予以展示,下面看到activeWorker端的相应代码就会了解原因了。
最后两行代码,将从activeWorker端接收到的信息通过PageConsole在网页中直接输出。
下面是activeWorker端接收数据时的相应代码:
如果消息类型为MessageChannel
,则先通过正则表达式取出打招呼者的姓名,然后再发送打招呼的信息。
如上面所述,evt.ports是一个数组,由于主线程一并将messageChannel.port2传递了过来,因此这里evt.ports[0]
就是所传递过来的端口。activeWorker端则使用它来反向传递信息。
通过这种双向通讯的方式,我们得以将Service Worker所反馈回来的信息放在网页中显示出来,从而克服了Service Worker不能直接访问DOM的不足。