You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox无法识别新Service Worker,Chrome正常的技术求助

Firefox 110.0.1下PWA Service Worker无法触发更新问题

我正在使用Firefox 110.0.1版本将Web应用升级为PWA,目前遇到以下问题:

  • Chrome中Service Worker首次安装正常,上传新版本时会提示用户,安装、激活及页面重载流程均正常,缓存修改也可生效,还能触发PWA安装提示;
  • Firefox始终无法检测到Service Worker已更新,updatefound监听器从未触发。

Service Worker代码

var staticCacheName = 'static';
var version = 'v1::90';
console.log("Hello there, this message is sent by a service worker " + version);

self.addEventListener('install', function (event) {
    console.log("Service worker installed");
    event.waitUntil(updateStaticCache());
});    

//if the browser detects that this is a new service worker, our app js will tell this new sw to activate (via "// tell new service worker to register itself")
self.addEventListener('message', (event) => {
    if (event.data === 'SKIP_WAITING') {
        console.log("skipWaiting 1");
        self.skipWaiting();
        console.log("skipWaiting 2");
    }
});

self.addEventListener('activate', function (event) {
    console.log("Service worker ACTIVATE");
    event.waitUntil(
        caches.keys()
        .then(function (keys) {
            return Promise.all(keys
                .filter(function (key) {
                    console.log("Cache version: " + key.indexOf(version));
                    return key.indexOf(version) !== 0;
                })
                .map(function (key) {
                    return caches.delete(key);
                })
            );
        })
    );
});

function updateStaticCache() {
    console.log("updateStaticCache()");
    return caches.open(version + staticCacheName)
        .then(function (cache) {
            return cache.addAll([
                '/offlineScreen.html',
                '/js/js01.js',
                '/js/js02.js',
                '/imgs/songbookdb-PWA-icon-512.png',
                '/imgs/songbookdb-PWA-icon-192.png',
                '/imgs/icon-ios-512.png'          
            ]);
        });
};

self.addEventListener('fetch', function (event) {
    console.log('Handling fetch event for', event.request.url);

    event.respondWith(            
        caches.match(event.request).then(function (response) {
            if (response) {
                console.log('Found response in cache:', response);

                return response;
            }

            console.log('No response found in cache. About to fetch from network...');

            return fetch(event.request).then(function (response) {
                console.log('Response from network is:', response);

                return response;
            }).catch(function (error) {                    
                console.error('Fetching failed:', error);

                return caches.match('/offlineScreen.html');
            });
        })
    );
});

前端JS代码

window.addEventListener("DOMContentLoaded", async event => {
    
    function invokeServiceWorkerUpdateFlow(registration) {              
        conf("There's an update for SongbookDB - Install now?", 'updateServiceWorker', registration);
    }

    // check if the browser supports serviceWorker at all
    if ('serviceWorker' in navigator) {

        console.log("service workers supported in browser");
        // wait for the page to load
        window.addEventListener('load', async () => {

            // register the service worker from the file specified
            navigator.serviceWorker.register("/serviceWorker.js");      
            //const registration = await navigator.serviceWorker.register('/serviceWorker.js'); 
            const registration = await navigator.serviceWorker.getRegistration();

            if (registration) { 
                
                console.log("service worker checks");

                // ensure the case when the updatefound event was missed is also handled
                // by re-invoking the prompt when there's a waiting Service Worker
                if (registration.waiting) {
                // invokeServiceWorkerUpdateFlow(registration)
                }

                // detect Service Worker update available and wait for it to become installed
                registration.addEventListener('updatefound', () => {
                    console.log("service worker update found");
                    if (registration.installing) {
                        // wait until the new Service worker is actually installed (ready to take over)
                        console.log("service worker installing");

                        registration.installing.addEventListener('statechange', () => {
                            if (registration.waiting) {
                                console.log("service worker waiting");
                                // if there's an existing controller (previous Service Worker), show the prompt
                                if (navigator.serviceWorker.controller) {
                                    invokeServiceWorkerUpdateFlow(registration)
                                } else {
                                    // otherwise it's the first install, nothing to do
                                    console.log('Service Worker initialized for the first time')
                                }
                            }
                        })
                    }
                })

                let refreshing = false;

                // detect controller change and refresh the page
                navigator.serviceWorker.addEventListener('controllerchange', () => {
                    if (!refreshing) {
                        window.location.reload()
                        refreshing = true
                    }
                })

                // For prompting user to install
                let deferredPrompt; // This variable will save the event for later use.
                window.addEventListener('beforeinstallprompt', (e) => {
                    console.log('install prompt');
                // Prevents the default mini-infobar or install dialog from appearing on mobile
                //e.preventDefault();
                // Save the event because you'll need to trigger it later.
                deferredPrompt = e;
                // Show your customized install prompt for your PWA
                // Your own UI doesn't have to be a single element, you
                // can have buttons in different locations, or wait to prompt
                // as part of a critical journey.
                //showInAppInstallPromotion();
                });
            }

        })
    }   
            
    function showResult(text) {
        //document.querySelector("output").innerHTML = text;
        console.log(text);
    }   

});

问题排查与修复建议

1. 修正Service Worker注册逻辑

当前代码中先调用register但不等待完成,直接用getRegistration()获取实例,Firefox中这个时机可能导致updatefound事件绑定不及时。
修复代码:

// 替换原注册相关代码
const registration = await navigator.serviceWorker.register('/serviceWorker.js');
// 移除navigator.serviceWorker.getRegistration()调用

2. 强制Firefox检查Service Worker更新

Firefox对Service Worker文件的缓存策略更严格,可能因旧文件缓存导致未触发更新:

  • 服务器端给serviceWorker.js设置缓存头:Cache-Control: no-cache, no-store, must-revalidate,避免浏览器缓存旧文件。
  • 在页面加载时主动触发更新检测:
// 获取registration后添加
registration.update();

3. 确保Service Worker文件有字节级差异

Firefox要求新版本Service Worker必须和旧版本有字节级不同才能被识别为更新,修改version变量后要确保服务器上的文件已同步更新,且确实产生了内容变化。

4. 验证缓存清理逻辑

激活阶段的缓存过滤条件key.indexOf(version) !== 0要确保缓存名称拼接正确(version + staticCacheName),避免旧缓存未被清理导致后续异常。


内容的提问来源于stack exchange,提问作者Shaun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:35:09