如何在同一范围内注册两个不同的service-worker?

问题描述 投票:0回答:3

我有一个

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();
  }
});

reactjs firebase firebase-cloud-messaging progressive-web-apps service-worker
3个回答
4
投票

好吧,花了几周时间,我想通了。因此,对于其他任何人,使用使用默认 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>' 
                            })

现在只需在索引文件中注册您的新服务人员,如图所示


0
投票

我认为做到这一点的一种方法就是只有 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-rewiredworkbox box 插件 代替


0
投票

您可能不需要两个不同的服务人员。

在 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)
}
© www.soinside.com 2019 - 2024. All rights reserved.