reactjs - 创建 React App PWA - 更改 Service Worker 的缓存策略

标签 reactjs caching create-react-app progressive-web-apps service-worker

我使用 CRA v4 创建了一个 PWA 模板,并通过注册启用了它附带的 Service Worker,因为我需要创建一个有关安装 PWA 的弹出通知。

应用必须通过灯塔测试才能兼容 PWA,以便浏览器能够触发检测用户是否已安装 PWA 所需的 beforeinstallprompt 事件监听器。

现在的问题是该服务工作人员正在使用缓存优先策略。因此,刷新页面不会触发更新,部署更新后,我会看到旧版本的应用程序。

如何更改 CRA v4 服务工作线程的缓存策略,以便用户只需刷新页面即可获取新版本的应用程序?

我也有兴趣了解为什么默认使用这种缓存优先策略。对我来说,用户必须关闭每个选项卡才能获得新版本似乎很糟糕。为什么没有更多人提出这个问题?这显然对用户不友好......

最佳答案

要更改策略,您需要实现自己的代码更改 service-worker.js 并可能 serviceWorkerRegistration.js。 (https://developers.google.com/web/tools/workbox/modules/workbox-strategies)

我实现自己的策略: 开头检查更新 如果开头更新有更新,则每 3 分钟检查一次更新 缓存并刷新网站。如果是在显示弹出窗口后要求刷新

serviceWorkerRegistration.ts


    /**
     * ...... previous code
     **/
    
    const CHECK_INTERVAL_TIME = 1000 * 60 * 3 // 3 min
    
    function registerValidSW(swUrl: string, config?: Config) {
        navigator.serviceWorker
            .register(swUrl)
            .then((registration) => {
                registration.onupdatefound = () => {
                    const installingWorker = registration.installing;
                    if (installingWorker == null) {
                        return;
                    }
                    installingWorker.onstatechange = () => {
                        if (installingWorker.state === 'installed') {
                            if (navigator.serviceWorker.controller) {
                                // At this point, the updated precached content has been fetched,
                                // but the previous service worker will still serve the older
                                // content until all client tabs are closed.
                                console.log(
                                    'New content is available and will be used when all ' +
                                    'tabs for this page are closed. See https://cra.link/PWA.'
                                );
    
                                // Execute callback
                                if (config && config.onUpdate) {
                                    config.onUpdate(registration);
                                }
                            } else {
                                // At this point, everything has been precached.
                                // It's the perfect time to display a
                                // "Content is cached for offline use." message.
                                console.info('Content is cached for offline use.');
    
                                // Execute callback
                                if (config && config.onSuccess) {
                                    config.onSuccess(registration);
                                }
                            }
                        }
                    };
                };
    
    
                /****************************
                 start new code
                 *****************************/
                registration.update().then(() => console.debug("Checked for update...")).catch(console.error)
    
                setInterval(() => {
                    console.debug("Checked for update...");
                    registration.update().catch(console.error);
                }, CHECK_INTERVAL_TIME);
    
                /****************************
                 end new code
                 *****************************/
            })
            .catch((error) => {
                console.error('Error during service worker registration:', error);
            });
    }
    

应用程序.tsx


    /**
     * ...... previous code
     **/
    function App() {
        const time = useRef(Date.now()); //can be let, depending of your logic
        useEffect(() => {
            serviceWorkerRegistration.register({
                onSuccess(registration) {
                    console.debug('serviceWorkerRegistration success')
                },
                onUpdate(registration) {
                    console.debug('serviceWorkerRegistration updated',Date.now()-time.current)
                    const  refresh=async ()=>{
                        await registration?.waiting.postMessage({type: 'SKIP_WAITING'}); //send message to update the code (stop waiting)
                        if ('caches' in window) { //delete cache, i think is no necessary but you lose nothing
                            const names = await caches.keys()
                            for (const name of names) {
                                await caches.delete(name)
                            }
                        }
                        window.location.reload();
                    }
                    if (Date.now()-time.current<=2000){
                        return refresh()
                    }
                    logicToShowPopup({
                        onClick: refresh
                    })
                }
            })
        }, [])
    
        return (<div>My App</div>)
    }

希望这能满足您的需求

关于reactjs - 创建 React App PWA - 更改 Service Worker 的缓存策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67520627/

相关文章:

javascript - 从 ReactJS 中的其他组件打开模式

Spring Redis 性能

asp.net - 与浏览器缓存相比,输出缓存的优势

webpack - 如何在基于 Create-React-App 的应用程序中配置 Workbox?

reactjs - 在github上发布react应用程序后出现空白页面

reactjs - 遍历 child ,找到最后一个元素

javascript - 在 React 中呈现嵌套 json 列表的任何有效方法?

javascript - Webpack 4 css 模块类型错误 : Cannot read property 'context' of undefined

javascript - Leaflet 标记弹出窗口中的缓存破坏

reactjs - 以非交互模式运行 CRA Jest