我有一个
service-worker.js
文件来制作我的 Reactjs 应用程序 PWA。我现在还想使用 FCM 添加推送通知,这要求我在公共文件夹中包含 firebase-messaging-sw.js
。因此,现在要使两者都起作用,就需要处于同一范围内。
但据我从该网站上的各种答案中读到,我们不能在同一范围内拥有两个不同的 Service Worker,那么我们如何将
service-worker.js
和 firebase-messaging-sw.js
结合起来,以便两者都能正常运行。其中一个答案建议我将 service-worker.js
重命名为 firebase-messaging-sw.js
,但这不起作用。我确实在 GitHub 上找到了一个成功的实现,但我不太理解https://github.com/ERS-HCL/reactjs-pwa-firebase .
如何让
service-worker.js
和 firebase-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 应用程序创建的 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 只是一个模板。您不能在 importScript
文件夹中的 service-worker.js
文件中使用 src
,因为这会引发 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>
就是这样。 firebase-messaging 和 PWA Service Worker 都可以工作
或者你也可以
您可以在公共文件夹中创建一个名为
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>'
})
现在只需在索引文件中注册您的新服务人员,如图所示
我认为做到这一点的一种方法就是只有 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
在你的package.json中,只需在react-script启动之前运行build-sw.js
"scripts": {
"start": "node build-sw.js && react-scripts start",
}
或者你可以使用 react-app-rewired 和 workbox box 插件 代替
您可能不需要两个不同的服务人员。
在 public 文件夹中,我创建了一个 sw.js 文件,在其中使用
importScripts
导入 Workbox,并添加了 FCM 的逻辑(即 push
和 notificationclick
的事件侦听器)。我从 head 标签中的 index.html
文件注册了这个 Service Worker。
为了获取令牌,我有一个 获取通知 按钮,具有以下
onClick
功能:
async function handleNotif() {
const registration = await navigator.serviceWorker.ready
const token = await getToken(messaging, {
serviceWorkerRegistration: registration,
vapidKey: 'my-vapid-key'
})
console.log(token)
}