我如何检测客户端是否有来自 Service Worker 的后台同步支持?

How can I detect if client has Background Sync support from the Service Worker?

我如何检测用户的浏览器是否在 service worker 中 支持后台同步?

我知道如何在应用端检测到这一点,但不知道如何从 Service Worker 内部检测。

基本上我使用的是 Workbox 的后台同步,它在 Chrome 和 Chrome 移动设备上运行良好。

我知道 Workbox 为不支持后台同步的浏览器提供后台同步 "fallback",但这不是很好用,所以我宁愿为不支持的浏览器完全禁用后台同步它。

这是我的 service worker 文件供参考。您可以看到如果浏览器不支持后台同步,我 不想 执行 3 个代码块(我用 *** I ONLY WANT TO RUN THIS CODE IF BACKGROUND SYNC IS SUPPORTED *** 标记了这些代码):

/*
 * This file (which will be your service worker)
 * is picked up by the build system ONLY if
 * quasar.conf > pwa > workboxPluginMode is set to "InjectManifest"
 */

/*
  config
*/

workbox.setConfig({ debug: false }) // disable workbox console logs
workbox.precaching.precacheAndRoute(self.__precacheManifest || [])

/*
basic routes
*/

workbox.routing.registerRoute(
  /^https:\/\/firestore.googleapis.com/,
  new workbox.strategies.NetworkFirst(),
  'GET'
);

workbox.routing.registerRoute(
  'https://myapp.com/posts',
  new workbox.strategies.NetworkFirst(),
  'GET'
);

workbox.routing.registerRoute(
  'https://myapp.com/favorites',
  new workbox.strategies.NetworkFirst(),
  'GET'
);

workbox.routing.registerRoute(
  /^http/,
  new workbox.strategies.StaleWhileRevalidate(),
  'GET'
);

/*
posts queue
*/

// *** I ONLY WANT TO RUN THIS CODE IF BACKGROUND SYNC IS SUPPORTED ***
const postsQueue = new workbox.backgroundSync.Queue('postsQueue', {
  maxRetentionTime: 24 * 60 * 365, // Retry for max of one year
  onSync: async ({queue}) => {
    let entry;
    while (entry = await queue.shiftRequest()) {
      try {
        await fetch(entry.request);
        // console.log('Replay successful for request', entry.request);

        const clients = await self.clients.matchAll({type: 'window'});
        for (const client of clients) {
          client.postMessage({
            msg: "offline-post-uploaded"
          })
        }        
      } catch (error) {
        console.error('Replay failed for request', entry.request, error);

        // Put the entry back in the queue and re-throw the error:
        await queue.unshiftRequest(entry);
        throw error;
      }
    }
    // console.log('Replay complete!');
  }
})

/*
update post queue
*/

// *** I ONLY WANT TO RUN THIS CODE IF BACKGROUND SYNC IS SUPPORTED ***
const updatePostQueue = new workbox.backgroundSync.Queue('updatePostQueue', {
  maxRetentionTime: 24 * 60 * 365, // Retry for max of one year
  onSync: async ({queue}) => {
    let entry;
    while (entry = await queue.shiftRequest()) {
      try {
        await fetch(entry.request);
        console.log('Replay successful for request', entry.request);     
      } catch (error) {
        console.error('Replay failed for request', entry.request, error);

        // Put the entry back in the queue and re-throw the error:
        await queue.unshiftRequest(entry);
        throw error;
      }
    }
    // console.log('Replay complete!');
  }
})

/*
events
*/

// *** I ONLY WANT TO RUN THIS CODE IF BACKGROUND SYNC IS SUPPORTED ***
self.addEventListener('fetch', (event) => {
  // console.log('event.request.url: ', event.request.url)
  if (event.request.url == 'https://myapp.com/createPost') {
    console.log('SW fetch createPost')
    // Clone the request to ensure it's safe to read when
    // adding to the Queue.
    if (!self.navigator.onLine) {
      console.log('SW device is offline')
      const promiseChain = fetch(event.request.clone()).catch((err) => {
        return postsQueue.pushRequest({request: event.request});
      });

      event.waitUntil(promiseChain);
    }
    else {
      console.log('SW device is online')
    }
  }
  else if (event.request.url.startsWith('https://myapp.com/updatePost')) {
    // Clone the request to ensure it's safe to read when
    // adding to the Queue.

    if (!self.navigator.onLine) {
      const promiseChain = fetch(event.request.clone()).catch((err) => {
        return updatePostQueue.pushRequest({request: event.request});
      }); 
      event.waitUntil(promiseChain);
    }
  }
});

self.addEventListener('notificationclick', event => {
  let notification = event.notification
  let action = event.action

  event.waitUntil(
    clients.matchAll()
      .then(clis => {
        let client = clis.find(cli => {
          return cli.visibilityState === 'visible'
        })

        if (client !== undefined) {
          client.navigate(notification.data.openUrl)
          client.focus()
        }
        else {
          clients.openWindow(notification.data.openUrl)
        }
        // notification.close()
      })
  )
})

self.addEventListener('notificationclose', event => {
  console.log('Notification was closed', event)
})

self.addEventListener('push', event => {
  console.log('Push Notification received: ', event)

  if (event.data) {
    console.log('event.data: ', event.data)
    let data = JSON.parse(event.data.text())
    console.log('data: ', data)

    console.log('showNotificationYo!')
    event.waitUntil(
      self.registration.showNotification(
        data.title,
        {
          body: data.body,
          icon: 'statics/icons/icon-128x128.png',
          badge: 'statics/icons/icon-128x128.png',
          image: data.imageUrl,
          data: {
            openUrl: data.openUrl
          }
        }
      )
    )
  }
})

workbox-background-sync 中使用的 feature detection 看起来像:

if ('sync' in self.registration) {
  // Background sync is natively supported.
}

您可以在自己的代码中做类似的事情来确定本机支持。