1

这是我在 sw 注册和刷新的 react spa 中的实现。

问题是当我在当前 sw 上触发手动更新并接受新版本(例如SKIP WAITING在活动 sw 中发送发布消息事件)并且在新 sw 被激活后我看不到controlling事件被触发

import { useEffect, useRef } from 'react';
import { Workbox, messageSW } from 'workbox-window';

const SW_PATH = `${process.env.PUBLIC_PATH}sw.js`;

export const useSWRegisterOnMount = (param: {
  createUIPrompt: (opts: { onAccept: () => void }) => void;
}): {
  wb?: InstanceType<typeof Workbox>;
  registration?: ServiceWorkerRegistration;
} => {
  const history = useHistory();

  const wb = useRef<InstanceType<typeof Workbox>>();
  const registration = useRef<ServiceWorkerRegistration>();

  useEffect(() => {
    /** https://developers.google.com/web/tools/workbox/guides/advanced-recipes */
    if ('serviceWorker' in navigator) {
      wb.current = new Workbox(SW_PATH, {
        scope: process.env.PUBLIC_PATH
      });

      const showSkipWaitingPrompt = (): void => {
        param.createUIPrompt({
          onAccept: () => {
            // this works perfectly when app is reopened
            // but in case of manual update service worker(workbox-window?) can skip emitting
            // of this "controlling" (also "activate") event but sw get activated in devtools 
            // application sw panel 
            wb.current?.addEventListener('controlling', () => {
              window.location.reload();
            });

            if (registration.current?.waiting) {
              messageSW(registration.current.waiting, {
                type: 'SKIP_WAITING'
              });
            }
          }
        });
      };

      wb.current.addEventListener('waiting', showSkipWaitingPrompt);
      wb.current.addEventListener('externalwaiting', showSkipWaitingPrompt);

      wb.current.register().then(swRegistration => {
        registration.current = swRegistration;
      });

      // https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#manual_updates
      setInterval(() => { // run manual updates
        registration.current?.update().then(() => {
          const sw = registration.current?.installing || registration.current?.waiting;

          if (sw) {
            showSkipWaitingPrompt();
          }
        });
      }, 1000 * 60 * 60);
    }
  }, []);

  return { wb: wb.current, registration: registration.current };
};

4

1 回答 1

1

您应该在服务人员端处理此事件:

addEventListener('message', (event) => {
    if (event.data.type === 'SKIP_WAITING') {
        skipWaiting();
    }
});
于 2021-11-16T12:08:09.970 回答