将自定义数据传递给 Service Worker 同步?

pes*_*tov 1 service-worker background-sync

我需要发出 POST 请求并发送一些数据。我正在使用服务工作者sync来处理离线情况。

但是有没有办法将 POST 数据传递给 service worker,让它再次发出相同的请求?

显然,当前的解决方案是将请求存储在某个客户端存储中,并且在客户端获得连接后 - 从存储中获取请求信息,然后发送它们。

还有更优雅的方式吗?

PS:我想过让服务工作者向应用程序代码发送消息,以便再次执行请求……但不幸的是,它不知道注册服务工作者的确切客户端:(

To-*_*xic 6

您可以使用获取同步

或者我使用 postmessage 来解决这个问题,我同意 indexedDB 看起来很麻烦。

  1. 首先,我从 html 发送消息。

    
    // send message to serviceWorker
    function sync (url, options) {
      navigator.serviceWorker.controller.postMessage({type: 'sync', url, options})
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我在 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)
    })
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在同步事件中,我获取数据并获取

    
    self.addEventListener('sync', event => {
      // get the data by tag
      const {url, options} = syncStore[event.tag]
      event.waitUntil(fetch(url, options))
    })
    
    Run Code Online (Sandbox Code Playgroud)

它在我的测试中运行良好,更重要的是您可以在获取后删除内存存储

更重要的是,您可能希望将结果发送回页面。我将通过 postmessage 以同样的方式做到这一点。

  1. 因为现在我必须相互通信,我会将功能同步更改为这种方式

    
    // 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])
    
    Run Code Online (Sandbox Code Playgroud) }) } // 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}) }
    Run Code Online (Sandbox Code Playgroud)
  2. 我还必须更改消息事件,以便我可以将端口传递给同步事件

    
    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)
        }
      }
    })
    
    Run Code Online (Sandbox Code Playgroud)
  3. 到目前为止,我们可以处理同步事件

    
    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})
        })
      )
    })
    
    Run Code Online (Sandbox Code Playgroud)

在末尾。不能用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')
  )
})
Run Code Online (Sandbox Code Playgroud)

当客户点击我们可以打开窗口。