为什么 404.html 在 service worker 缓存期间导致未捕获的错误?

Why 404.html causes uncaught error during service worker caching?

我在我的 Web 应用程序中实现了服务工作者,并尝试缓存所有 html、css、js 和图像文件。我的托管服务是firebase,在成功部署后我测试了服务是否可用以及文件是否会被缓存,不幸的是会出现以下错误。

服务-worker.js

let cacheName = 'my-tools-v1';
let filesToCache = [
    '/',
    '/css/app.css',
    '/css/diffview.css',
    '/css/json-lint.css',
    '/css/materialize.css',
    '/images/favicon.png',
    '/images/icons/apple-touch-icon.png',
    '/images/icons/apple-touch-icon-57x57.png',
    '/images/icons/apple-touch-icon-72x72.png',
    '/images/icons/apple-touch-icon-76x76.png',
    '/images/icons/apple-touch-icon-114x114.png',
    '/images/icons/apple-touch-icon-120x120.png',
    '/images/icons/apple-touch-icon-144x144.png',
    '/images/icons/apple-touch-icon-152x152.png',
    '/images/icons/apple-touch-icon-180x180.png',
    '/images/icons/icon-72x72.png',
    '/images/icons/icon-96x96.png',
    '/images/icons/icon-128x128.png',
    '/images/icons/icon-144x144.png',
    '/images/icons/icon-152x152.png',
    '/images/icons/icon-192x192.png',
    '/images/icons/icon-384x384.png',
    '/images/icons/icon-512x512.png',
    '/js/index.js',
    '/js/css-lint.js',
    '/js/difflib.js',
    '/js/diffview.js',
    '/js/ipsum-generator.js',
    '/js/json2.js',
    '/js/json-lint.js',
    '/js/jsonlint.js',
    '/js/lorem-ipsum.js',
    '/js/materialize.js',
    '/js/visual-difference.js',
    '/bower_components/codemirror/lib/codemirror.js',
    '/bower_components/codemirror/mode/javascript/javascript.js',
    '/bower_components/codemirror/mode/css/css.js',
    '/bower_components/codemirror/addon/edit/matchbrackets.js',
    '/bower_components/codemirror/addon/comment/continuecomment.js',
    '/bower_components/codemirror/addon/comment/comment.js',
    '/bower_components/ft-csslint/dist/csslint.js',
    '/bower_components/jquery/dist/jquery.slim.min.js',
    '/bower_components/kanye-ipsum/dist/jquery.kanye-ipsum.min.js',
    '/bower_components/codemirror/lib/codemirror.css',
    '/index.html',
    '/css-lint.html',
    '/ipsum-generator.html',
    '/json-lint.html',
    '/visual-difference.html',
    '/notfound.html',
    '/404.html'
];

self.addEventListener('install', (e) => {
    console.log('[ServiceWorker] Install');

    e.waitUntil(
        caches.open(cacheName).then((cache) => {
            console.log('[ServiceWorker] Caching app shell');
            return cache.addAll(filesToCache);
        })
    );
});

self.addEventListener('activate', (e) => {
    console.log('[ServiceWorker] Activate');

    e.waitUntil(
        caches.keys().then(function(keyList) {
            return Promise.all(keyList.map((key) => {
                if (key !== cacheName) {
                    console.log('[ServiceWorker] Removing old cache', key);
                    return caches.delete(key);
                }
            }));
        })
    );
});

self.addEventListener('fetch', (e) => {
    console.log('[ServiceWorker] Fetch', e.request.url);

    e.respondWith(
        caches.match(e.request).then((response) => {
            return response || fetch(e.request);
        })
    );
});

然后所有的文件都因为这个原因没有被缓存。但是,如果我删除 404.html 或将其重命名为其他名称,Service Worker 将正常工作并且所有文件都将被缓存。同样奇怪的是,在我的本地服务器中,服务工作者工作并缓存 404.html 但它在 firebase 中失败。

为什么 404.html 在 service worker 缓存期间导致未捕获的错误?我该如何解决?

Cache.addAll() 是全有或全无 API。如果任何响应不在 200 HTTP 状态代码范围内,则不会缓存任何内容。

cache.addAll will reject if any of the resources fail to cache. This means the service worker will only install if all of the resources in cache.addAll have been cached.

Firebase returns 404 Not Found 用于 /404.html 文件。

解决此问题的一种方法是像您一样拥有一个文件 /notfound.html,然后在需要时 fetch 中 return。

您不能将 404 错误 html 页面添加到缓存中,如果它 returns 404 https 状态(应该如此)。

但是即使网站处于离线模式,您仍然可以使用 service worker 创建 404 错误页面并将其用于不在缓存中的网站。

fetch() 请求后使用 catch() 并创建全新的响应

简约示例:

// self is ServiceWorkerGlobalScope
self.addEventListener( 'fetch', function ( /** @type {FetchEvent} */ event )
{

    //const caches = ( event.target.caches );

    event.respondWith(
        caches.match( event.request ).then( ( /** @type {Response | undefined} */ response ) =>
        {

            if ( response ) {
                return response;
            }

            return fetch( event.request.clone() ).then( ( /** @type {Response} */ response ) =>
            {

                //…

                return response;
            }
            ).catch( () =>
            {
                return new Response( '<h1>404 - Not found</h1><p>more HTML here …</p>', {
                    status: 404,
                    statusText: 'Not found',
                    headers: new Headers( {
                        'Content-Type': 'text/html'
                    } )
                } );

            } );

        }
        )
    );

} );