问题描述
我需要发出 POST 请求并发送一些数据.我正在使用服务工作者 sync
来处理离线情况.
但是有没有办法将 POST 数据传递给 service worker,让它再次发出相同的请求?
显然,当前的解决方案是将请求存储在某些客户端存储中,并且在客户端获得连接后 - 从存储中获取请求信息,然后发送它们.
还有更优雅的方式吗?
PS:我曾想过让 service worker 向应用程序代码发送消息,以便它再次执行请求……但不幸的是,它不知道注册 service worker 的确切客户端:(
您可以使用 fetch-sync
或者我使用 postmessage 来解决这个问题,我同意 indexedDB 看起来很麻烦.
首先,我从 html 发送消息.
//向 serviceWorker 发送消息功能同步(网址,选项){navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})}我在 serviceworker 中收到此消息,然后将其存储.
const syncStore = {}self.addEventListener('message', event => {if(event.data.type === '同步') {//获取一个唯一的 id 来保存数据const id = uuid()syncStore[id] = event.data//注册一个同步并将id作为标签传递给它以获取数据self.registration.sync.register(id)}控制台日志(事件数据)})在同步事件中,我获取数据并获取
self.addEventListener('sync', event => {//通过标签获取数据const {url, options} = syncStore[event.tag]event.waitUntil(fetch(url, options))})
在我的测试中效果很好,更重要的是你可以在获取后删除内存
此外,您可能希望将结果发送回页面.我将通过 postmessage 以同样的方式做到这一点.
由于现在我必须相互通信,我将功能同步更改为这种方式
//使用消息通道进行通信sendMessageToSw (msg) {返回新的承诺((解决,拒绝)=> {//创建消息通道const msg_chan = new MessageChannel()//用于接收来自 Service Worker 的消息回复的处理程序msg_chan.port1.onmessage = 事件 =>{如果(事件.数据.错误){拒绝(事件.数据.错误)} 别的 {解决(事件数据)}}navigator.serviceWorker.controller.postMessage(msg, [msg_chan.port2])
})}//向 serviceWorker 发送消息//你可以看到我添加了一个解析参数//这是用来告诉 serviceworker 如何解析我们的数据功能同步(网址,选项,解析){return sendMessageToSw({type: 'sync', url, options, parse})}
我还必须更改消息事件,以便我可以将端口传递给同步事件
self.addEventListener('message', event => {if(isObject(event.data)) {if(event.data.type === '同步') {//这样,你就可以决定你的标签const id = event.data.id ||uuid()//将端口传递到内存中syncStore[id] = Object.assign({port: event.ports[0]}, event.data)self.registration.sync.register(id)}}})到目前为止,我们可以处理同步事件
self.addEventListener('sync', event => {const {url, options, port, parse} = syncStore[event.tag] ||{}//删除内存删除syncStore[event.tag]event.waitUntil(fetch(url, options).then(响应 => {//克隆响应,因为如果再次解析它将无法解析const copy = response.clone()如果(响应.确定){//随心所欲地解析它复制[解析]().then(数据 => {//当成功 postmessage 返回port.postMessage(数据)})} 别的 {port.postMessage({error: response.status})}}).catch(错误 => {port.postMessage({错误:error.message})}))})
最后.不能直接用postmessage发送响应,因为是非法的,所以需要解析一下,比如text、json、blob等,我觉得就够了.
正如您所提到的,您可能想要打开窗口.我建议您可以使用 serviceworker 发送通知.
self.addEventListener('push', function (event) {const title = '我是一个他妈的测试'常量选项 = {身体:'是的,它有效.',}event.waitUntil(self.registration.showNotification(title, options))})self.addEventListener('notificationclick', function (event) {event.notification.close()事件.等待直到(clients.openWindow('https://yoursite.com'))})
当客户点击我们可以打开窗口.
I need to make a POST request and send some data. I'm using the service worker sync
to handle offline situation.
But is there a way to pass the POST data to the service worker, so it makes the same request again?
Cause apparently the current solution is to store requests in some client side storage and after client gets connection - get the requests info from the storage and then send them.
Any more elegant way?
PS: I thought about just making the service worker send message to the application code so it does the request again ... but unfortunately it doesn't know the exact client that registered the service worker :(
You can use fetch-sync
or i use postmessage to fix this problem, which i agree that indexedDB looks trouble.
first of all, i send the message from html.
// send message to serviceWorker function sync (url, options) { navigator.serviceWorker.controller.postMessage({type: 'sync', url, options}) }
i got this message in serviceworker, and then i store it.
const syncStore = {} self.addEventListener('message', event => { if(event.data.type === 'sync') { // get a unique id to save the data const id = uuid() syncStore[id] = event.data // register a sync and pass the id as tag for it to get the data self.registration.sync.register(id) } console.log(event.data) })
in the sync event, i got the data and fetch
self.addEventListener('sync', event => { // get the data by tag const {url, options} = syncStore[event.tag] event.waitUntil(fetch(url, options)) })
it works well in my test, what's more you can delete the memory store after the fetch
what's more, you may want to send back the result to the page. i will do this in the same way by postmessage.
as now i have to communicate between each other, i will change the fucnction sync into this way
// use messagechannel to communicate sendMessageToSw (msg) { return new Promise((resolve, reject) => { // Create a Message Channel const msg_chan = new MessageChannel()
// Handler for recieving message reply from service worker msg_chan.port1.onmessage = event => { if(event.data.error) { reject(event.data.error) } else { resolve(event.data) } } navigator.serviceWorker.controller.postMessage(msg, [msg_chan.port2])
})}// send message to serviceWorker// you can see that i add a parse argument// this is use to tell the serviceworker how to parse our datafunction sync (url, options, parse) { return sendMessageToSw({type: 'sync', url, options, parse})}
i also have to change the message event, so that i can pass the port to sync event
self.addEventListener('message', event => { if(isObject(event.data)) { if(event.data.type === 'sync') { // in this way, you can decide your tag const id = event.data.id || uuid() // pass the port into the memory stor syncStore[id] = Object.assign({port: event.ports[0]}, event.data) self.registration.sync.register(id) } } })
up to now, we can handle the sync event
self.addEventListener('sync', event => { const {url, options, port, parse} = syncStore[event.tag] || {} // delete the memory delete syncStore[event.tag] event.waitUntil(fetch(url, options) .then(response => { // clone response because it will fail to parse if it parse again const copy = response.clone() if(response.ok) { // parse it as you like copy[parse]() .then(data => { // when success postmessage back port.postMessage(data) }) } else { port.postMessage({error: response.status}) } }) .catch(error => { port.postMessage({error: error.message}) }) ) })
At the end. you cannot use postmessage to send response directly.Because it's illegal.So you need to parse it, such as text, json, blob, etc. i think that's enough.
As you have mention that, you may want to open the window.i advice that you can use serviceworker to send a notification.
self.addEventListener('push', function (event) {
const title = 'i am a fucking test'
const options = {
body: 'Yay it works.',
}
event.waitUntil(self.registration.showNotification(title, options))
})
self.addEventListener('notificationclick', function (event) {
event.notification.close()
event.waitUntil(
clients.openWindow('https://yoursite.com')
)
})
when the client click we can open the window.
这篇关于将自定义数据传递给 Service Worker 同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!