我如何检测客户端是否有来自 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.
}
您可以在自己的代码中做类似的事情来确定本机支持。
我如何检测用户的浏览器是否在 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.
}
您可以在自己的代码中做类似的事情来确定本机支持。