如何在同一个作用域中注册两个不同的service-worker ?



我有一个service-worker.js文件,使我的reactjs应用程序PWA。我现在还想使用FCM添加推送通知,这需要我在公共文件夹中有firebase-messaging-sw.js。因此,现在要使两者都工作,它们将需要在相同的作用域。

但就我在这个网站上看到的各种答案而言,我们不能在同一个范围内有两个不同的服务工作者,那么我们如何将service-worker.jsfirebase-messaging-sw.js结合起来,使两者都能正常工作。其中一个答案建议我将service-worker.js重命名为firebase-messaging-sw.js,这不起作用。我确实在GitHub上找到了一个成功的实现,我不太了解https://github.com/ERS-HCL/reactjs-pwa-firebase。

我怎么能让service-worker.jsfirebase-messaging-sw.js一起工作?

firebase-messaging-sw.js

// Scripts for firebase and firebase messaging
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js");
// Initialize the Firebase app in the service worker by passing the generated config
const firebaseConfig = {
apiKey: "xxxx",
authDomain: "xxxx.firebaseapp.com",
projectId: "xxxx",
storageBucket: "xxxx",
messagingSenderId: "xxxx",
appId: "xxxx",
measurementId: "xxxx"
}
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = firebase.messaging();

self.addEventListener("notificationclick", function (event) {
console.debug('SW notification click event', event)
const url = event.notification.data.link
event.waitUntil(
clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
})

messaging.onBackgroundMessage(async function(payload) {
console.log("Received background message ", payload)
const notificationTitle = payload.notification.title
const notificationOptions = {
body: payload.notification.body,
icon: './logo192.png',
badge: './notification-badgex24.png',
data: {
link: payload.data?.link
}
}
self.registration.showNotification(notificationTitle, notificationOptions)
})

service-worker.js

import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
clientsClaim();
const fileExtensionRegexp = new RegExp('/[^/?]+\.[^/]+$');
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false;
} // If this is a URL that starts with /_, skip.
if (url.pathname.startsWith('/_')) {
return false;
} // If this looks like a URL for a resource, because it contains // a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false;
} // Return true to signal that we want to use the handler.
return true;
},
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
);
registerRoute(
// Add in any other file extensions or routing criteria as needed.
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({
cacheName: 'images',
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }),
],
})
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});

好吧,花了几个星期后,我弄明白了。所以对于其他人来说,使用默认的create react app创建的service worker,请按照下面的步骤操作。

首先在公共文件夹中创建firebase-messaging-sw.js,并放置与问题中相同的代码,但这次也添加importScripts("/service-worker.js")

// Scripts for firebase and firebase messaging
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js")
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js")
importScripts(`/service-worker.js`) // <- here notice this
.
.
.

导入将在构建步骤之后导入service-worker.js。src目录下的service worker只是一个模板。你不能在src文件夹下的service-worker.js文件中使用importScript,因为这会抛出importScripts is not defined错误。

构建步骤后的构建文件夹:

└── build/
├── static/
│   ├── .
│   ├── .
│   └── .
├── index.html
├── firebase-messaging-sw.js
├── service-worker.js
├── .
└── .

在index.html中添加

<script>
if ('serviceWorker' in navigator){
navigator.serviceWorker.register('/firebase-messaging-sw.js')
.then(reg => console.debug("Service worker registered sucessfully"))
}

</script>

就是这样。火基消息传递和PWA服务工作者都可以工作

或者

您可以在公共文件夹中创建一个名为sw.js的新文件,并使用imporScript()导入firebase-messaging-sw.js和默认的service-worker.js

确保在firebase的getToken中传入service worker注册

const registration = await navigator.serviceWorker.register('/sw.js')

const currentToken = await getToken(messaging, { 
serviceWorkerRegistration: registration,
vapidKey: '<VAPID_KEY>' 
})

现在只需在索引文件中注册新的service worker,如下所示

我认为这样做的一种方法是只有firebase-messaging-sw.js文件,并使用workbox注入你的service-worker.js到它

https://developer.chrome.com/docs/workbox/precaching-with-workbox/

例如:

// build-sw.js
import {injectManifest} from 'workbox-build';
injectManifest({
swSrc: './src/sw.js',
swDest: './dist/firebase-messaging-sw.js',
globDirectory: './dist',
globPatterns: [
'**/*.js',
'**/*.css',
'**/*.svg'
]
});

和所有firebase配置必须在sw.js中才能写入firebase-messaging-sw.js

和在您的包中。Json只需在react-script开始前运行build-sw.js

"scripts": {
"start": "node build-sw.js && react-scripts start",
}

或者你可以使用react-app-rewired和workbox box插件来代替

相关内容

最新更新