我需要发出 POST 请求并发送一些数据。我正在使用服务人员sync
来处理离线情况。
但是有没有办法将 POST 数据传递给服务人员,所以它再次发出相同的请求?
显然,当前的解决方案是将请求存储在某些客户端存储中,并在客户端获得连接后 - 从存储中获取请求信息,然后发送它们。
还有更优雅的方式吗?
PS:我想过让服务工作者向应用程序代码发送消息,以便它再次发出请求......但不幸的是它不知道注册服务工作者的确切客户端:(
我需要发出 POST 请求并发送一些数据。我正在使用服务人员sync
来处理离线情况。
但是有没有办法将 POST 数据传递给服务人员,所以它再次发出相同的请求?
显然,当前的解决方案是将请求存储在某些客户端存储中,并在客户端获得连接后 - 从存储中获取请求信息,然后发送它们。
还有更优雅的方式吗?
PS:我想过让服务工作者向应用程序代码发送消息,以便它再次发出请求......但不幸的是它不知道注册服务工作者的确切客户端:(
您可以使用fetch-sync
或者我使用 postmessage 来解决这个问题,我同意 indexedDB 看起来很麻烦。
首先,我从 html 发送消息。
// send message to serviceWorker
function sync (url, options) {
navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})
}
我在 serviceworker 中收到此消息,然后将其存储。
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)
})
在同步事件中,我得到了数据并获取
self.addEventListener('sync', event => {
// get the data by tag
const {url, options} = syncStore[event.tag]
event.waitUntil(fetch(url, options))
})
它在我的测试中运行良好,而且您可以在获取后删除内存存储
更重要的是,您可能希望将结果发送回页面。我将通过 postmessage 以相同的方式执行此操作。
因为现在我必须相互交流,我将把功能同步改成这种方式
// 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 data
function sync (url, options, parse) {
return sendMessageToSw({type: 'sync', url, options, parse})
}
我还必须更改消息事件,以便我可以通过端口同步事件
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)
}
}
})
到目前为止,我们可以处理同步事件
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})
})
)
})
在最后。你不能使用postmessage直接发送响应。因为它是非法的。所以你需要解析它,例如text,json,blob等。我认为这就足够了。
正如您所提到的,您可能想要打开窗口。我建议您可以使用 serviceworker 发送通知。
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')
)
})
当客户端点击我们可以打开窗口。
为了与服务人员交流,我使用了一个技巧:
在 fetch eventlistener 我把这个:
self.addEventListener('fetch', event => {
if (event.request.url.includes("sw_messages.js")) {
var zib = "some data";
event.respondWith(new Response("window.msg=" + JSON.stringify(zib) + ";", {
headers: {
'Content-Type': 'application/javascript'
}
}));
}
return;
});
然后,在主 html 中我只添加:
<script src="sw_messages.js"></script>
当页面加载时,全局变量 msg 将包含(在本例中)“一些数据”。