这是我在 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 };
};