前言
serviceWorker 的能力决定它要处理的事情,网站页面的部分逻辑处理会转移到 serviceWorker 层进行处理,这里就要页面层和 serviceWorker 层进行交互来实现消息通讯。
下面就说一下两个环境下的消息通讯。
窗口向 serviceWorker 通讯
这里列举出窗口层到 serviceWorker 层的通讯方法。
1. ServiceWorker.postMessage
页面层可以通过 ServiceWorker
接口的 postMessage
来实现页面到 serviceWorker 环境的通讯。
ServiceWorker 接口的获取:
ServiceWorker 接口获取有两种方式
navigator.serviceWorker.controller
,常用这种方式。navigator.serviceWorker.ready.then(swReg => swReg[state])
:state 为{installing, waiting, active}
。
当 postMessage 后,serviceWorker 环境采用 onmessage 事件进行处理。
发送消息:
发送消息实现:
index.html
// 页面 window 环境if(navigator.serviceWorker.controller) { // 需要判断是否受控navigator.serviceWorker.controller.postMessage('消息'); // postMessage 的第一个参数可以是由结构化克隆算法处理的任何值或JavaScript对象,也包括循环引用。
}
sw.js
// serviceWorker 环境self.addEventListener("message", e => {console.log("message", e);// 从 e.data 里面取 postMessage 过来的数据
})
接收消息:
页面层 window 环境下接收消息需要在 ServiceWorkerContainer
接口上监听 onmessage:
navigator.serviceWorker.onmessage
serviceWorker 层做定向 Client 的获取有以下方式:
// 1. 通过 e.source.id 来定向发送消息self.addEventListener("message", e => {const client = await self.clients.get(e.source.id);client.postMessage('发给页面层的消息');
})// 2. 直接 e.source 来定向发送消息self.addEventListener("message", e => {e.source.postMessage('发给页面层的消息');
})
2. ServiceWorkerRegistration.sync.register
第二种方式,是使用 sync 的方式来实现页面层到 serviceWorker 层的通讯。
这种通讯的弊端是单向的,且不可控。
但优势也很明显,对于后台同步十分有用,一旦注册 sync 在 online 的状态下会立即触发 serviceWorker 环境下的 onsync 事件,serviceWorker 可根据具体逻辑处理,直到 e.waitUntil
返回 Promise.resolve()
才会完成 sync,并把 sync 的 tag 清除,否则会一直按照某个周期执行,知道 e.lastChance == true
。
// 页面层环境navigator.serviceWorker.ready.then(swReg => {swReg.sync.register('同步tag')
})
// serviceWorker 层环境self.addEventListener("sync", e => {if(e.tag == '同步tag') {e.waitUntil(new Promise((res, rej) => {// 逻辑处理 ...return res();}))}
})
3. MessageChannel
MessageChannel 是一个点对点的消息通道,可以很方便的实现消息的双向通讯。
构造函数:
构造函数很简单,不需要任何参数
var channel = new MessageChannel();
属性:
- MessageChannel.port1
- MessageChannel.port2
属性中的两个 port 为 MessagePort 接口实现,具备以下方法:
- postMessage
- start
- close
具备事件监听:MessagePort.onmessage。
这里发消息时,主要以环境下的 postMessage 配合使用。
// 页面层环境if(navigator.serviceWorker.controller) {var c = new MessageChannel();c.port1.onmessage = e => {// 收到传给 port1 的消息}// 向 port2 发送消息navigator.serviceWorker.controller.postMessage('消息', [c.port2])
}
// serviceWorker 层self.addEventListener("message", e => {// 从 e.ports 里取 MessagePorte.ports[0] && e.ports[0].postMessage('向port1发送消息')
})
注意:MessageChannel 创建的通道会受 serviceWorker 的 stopWorker 影响,导致 MessageChannel 通道 close,也就是表现为通道只能用一次。所以使用 MessageChannel 通讯时,每次都要创建新的通道。
serviceWorker 向窗口通讯
上面说的是窗口页面层向 serviceWorker 环境的通讯,同样 serviceWorker 环境层也需要向页面层通讯。
在 serviceWorker 环境下主要有两种向页面层通讯的方式。
1. BroadcastChannel
第一种是 BroadcastChannel,也就是广播信道通讯。
构造函数:
构造函数很简单,channel
参数为一个字符串。
var channel = new BroadcastChannel(channel);
属性:
BroadcastChannel.name
:构造时的信道名。
事件:
- onmessage
- onmessageerror
方法:
- postMessage()
- close()
// 页面层var bc1 = new BroadcastChannel('c1');bc1.onmessage = e => {// 页面层收到广播,逻辑处理
}
// serviceWorker 层var bc1 = new BroadcastChannel('c1');bc1.postMessage('发送广播消息');
2. client.postMessage
第二种就是获取相应的 client 进行 postMessage。
如果从 onmessage 中,是可以获取到相应的 sorce client 的,从而进行双向通讯。但在自发情况下,只能对所有 client 进行广播通讯。
// serviceWorker 环境clients.matchAll({type: "window"
})
.then(windows => {for (const win of windows) {win.postMessage('发送消息到页面');}
});
博客名称:王乐平博客
CSDN博客地址:http://blog.csdn.net/lecepin