admin管理员组

文章数量:1327486

I am using service workers to create an offline page for my website.

At the moment I am saving offline.html into cache so that the browser can show this file if there is no interent connection.

In the fetch event of my service worker I attempt to load index.html, and if this fails (no internet connection) I load offline.html from cache.

However, whenever I check offline mode in developer tools and refresh the page index.html still shows...

The request isn't failing, and it looks like index.html is being cached even though I didn't specify it to be.

Here is my HTML for index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Service Workers - Test</title>
</head>
<body>
    <h1> Online page! </h1>
    <h3> You are connected to the internet. </h3>
</body>
<script>

if ('serviceWorker' in navigator)
{
    navigator.serviceWorker.register('service-worker.js');
}
</script>

</html>

Here is my HTML for offline.html:

<!DOCTYPE html>
<html>
<head>
    <title>You are Offline - Service Workers - Test</title>
</head>
<body>
    <h1> Wele to the Offline Page!</h1>
    <h2> You are not connected to the internet but you can still do certain things offline. </h2>

</body>

</html>

Here is my javascript for service-worker.js:

const PRECACHE = "version1"
const CACHED = ["offline.html"];

// Caches "offline.html" incase there is no internet
self.addEventListener('install', event => {
    console.log("[Service Worker] Installed");
    caches.delete(PRECACHE)
    event.waitUntil (
        caches.open(PRECACHE)
            .then(cache => cache.addAll(CACHED))
            .then(    _ => self.skipWaiting())
    );
});

// Clears any caches that do not match this version
self.addEventListener("activate", event => {
    event.waitUntil (
        caches.keys()
            .then(keys => {
                return Promise.all (
                    keys.filter(key => {
                        return !key.startsWith(PRECACHE);
                    })
                    .map(key => {
                        return caches.delete(key);
                    })
                );
            })
            .then(() => {
                console.log('[Service Worker] Cleared Old Cache');
            })
    );
});

this.addEventListener('fetch', function(event) {
    if (event.request.method !== 'GET') return;

    console.log("[Service Worker] Handling Request ");

    // If the request to `index.html` works it shows it, but if it fails it shows the cached version of `offline.html`

    // This isn't working because `fetch` doesn't fail when there is no internet for some reason...

    event.respondWith (
        fetch(event.request)
            .then(response => {
                console.log("[Service Worker] Served from NETWORK");
                return response;
            }, () => {
                console.log("[Service Worker] Served from CACHE");
                return catches.match(event.request.url + OFFLINE_URL);
            })
    );
});

I am running a server using python's simple http server like so:

python -m SimpleHTTPServer

Does anyone know why the offline page isn't working and how I can fix this?

Thanks for the help, David

EDIT:

These images are showing that index.html (localhost) is still loading without internet which means it must be cached.

Edit 2:

I've tried to add no-cache to the fetch of index.html and it still is fetching index.html when I have offline checked.

fetch(event.request, {cache: "no-cache"}) ...

I am using service workers to create an offline page for my website.

At the moment I am saving offline.html into cache so that the browser can show this file if there is no interent connection.

In the fetch event of my service worker I attempt to load index.html, and if this fails (no internet connection) I load offline.html from cache.

However, whenever I check offline mode in developer tools and refresh the page index.html still shows...

The request isn't failing, and it looks like index.html is being cached even though I didn't specify it to be.

Here is my HTML for index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Service Workers - Test</title>
</head>
<body>
    <h1> Online page! </h1>
    <h3> You are connected to the internet. </h3>
</body>
<script>

if ('serviceWorker' in navigator)
{
    navigator.serviceWorker.register('service-worker.js');
}
</script>

</html>

Here is my HTML for offline.html:

<!DOCTYPE html>
<html>
<head>
    <title>You are Offline - Service Workers - Test</title>
</head>
<body>
    <h1> Wele to the Offline Page!</h1>
    <h2> You are not connected to the internet but you can still do certain things offline. </h2>

</body>

</html>

Here is my javascript for service-worker.js:

const PRECACHE = "version1"
const CACHED = ["offline.html"];

// Caches "offline.html" incase there is no internet
self.addEventListener('install', event => {
    console.log("[Service Worker] Installed");
    caches.delete(PRECACHE)
    event.waitUntil (
        caches.open(PRECACHE)
            .then(cache => cache.addAll(CACHED))
            .then(    _ => self.skipWaiting())
    );
});

// Clears any caches that do not match this version
self.addEventListener("activate", event => {
    event.waitUntil (
        caches.keys()
            .then(keys => {
                return Promise.all (
                    keys.filter(key => {
                        return !key.startsWith(PRECACHE);
                    })
                    .map(key => {
                        return caches.delete(key);
                    })
                );
            })
            .then(() => {
                console.log('[Service Worker] Cleared Old Cache');
            })
    );
});

this.addEventListener('fetch', function(event) {
    if (event.request.method !== 'GET') return;

    console.log("[Service Worker] Handling Request ");

    // If the request to `index.html` works it shows it, but if it fails it shows the cached version of `offline.html`

    // This isn't working because `fetch` doesn't fail when there is no internet for some reason...

    event.respondWith (
        fetch(event.request)
            .then(response => {
                console.log("[Service Worker] Served from NETWORK");
                return response;
            }, () => {
                console.log("[Service Worker] Served from CACHE");
                return catches.match(event.request.url + OFFLINE_URL);
            })
    );
});

I am running a server using python's simple http server like so:

python -m SimpleHTTPServer

Does anyone know why the offline page isn't working and how I can fix this?

Thanks for the help, David

EDIT:

These images are showing that index.html (localhost) is still loading without internet which means it must be cached.

Edit 2:

I've tried to add no-cache to the fetch of index.html and it still is fetching index.html when I have offline checked.

fetch(event.request, {cache: "no-cache"}) ...
Share Improve this question edited Aug 2, 2017 at 11:51 David Callanan asked Aug 1, 2017 at 20:41 David CallananDavid Callanan 5,82015 gold badges76 silver badges123 bronze badges 6
  • 1 Is the worker throwing any warnings and/or errors? Is it installed? Can you see the cache in dev tools? under the application tab? – Schrodinger's cat Commented Aug 2, 2017 at 9:23
  • @Schrodinger'scat The service worker is installed and I refreshed the page again to make sure the service worker handled the fetch of the page. In the cache there was index.html even though I didn't cache it. – David Callanan Commented Aug 2, 2017 at 9:35
  • Ok! Among other things to look for, you have typos it is caches.match on the fetch event and not catches. Also, If you have an update on reload checked, uncheck it, as when you go offline, the browser searches for a new service worker and the request fails.. – Schrodinger's cat Commented Aug 2, 2017 at 11:04
  • @Schrodinger'scat I've fixed all my typos but that didn't fix it. When I refresh the page it loads index.html which means that page must be cached even when I didn't include it in my cache list CACHED – David Callanan Commented Aug 2, 2017 at 11:19
  • It looks like the index.html is served from disk cache and bypasses the service worker for some reason - noticed this after trying this out on my machine - This can be circumvented by checking disable cache, but that is a hack – Schrodinger's cat Commented Aug 2, 2017 at 13:34
 |  Show 1 more ment

3 Answers 3

Reset to default 4

I think we have all forgotten how the network request works from a browser's point of view.

The issue here is, index.html is served from the disk cache when the service worker intercepts requests. browser ===> Service Worker ===>fetch event

inside the fetch event, we have ,

  • Check If there is network connectivity
    • If there is, fetch from network and respond
    • Else, fetch from cache and respond

Now, how does

If there is network connectivity, fetch from network work?

Service Worker OnFetch ===> Check in Disk Cache ===>Nothing? Fetch Online

The page being fetched here, is index.html

and the cache-control headers for index.html ,

Do Not Specify a no-cache

Hence the whole issue of the offline page not showing up.

Solution

  • Set a cache-control header with limiting values for index.html - On the server side
  • Or, add headers in the fetch request to the effect

    • pragma:no-cache
    • cache-control:no-cache

How Do I add these headers to fetch?

Apparently, fetch and the browser have their own reservations about the request body when it es to a GET

Also, weirdness and utter chaos happens If you reuse the event.request object, for a fetch request, and add custom headers. The chaos is a list of Uncaught Exceptions due to the fetch event's request.mode attribute , which bars you from adding custom headers to a fetch when under a no-cors or a navigate mode.

Our goal is to :

Identify that the browser is truly offline and then serve a page that says so

Here's How:

Check If you can fetch a dummy html page say test-connectivity.html under your origin, with a custom cache: no-cache header. If you can, proceed, else throw the offline page

self.addEventListener( 'fetch', ( event ) => {
let headers = new Headers();
headers.append( 'cache-control', 'no-cache' );
headers.append( 'pragma', 'no-cache' );
var req = new Request( 'test-connectivity.html', {
    method: 'GET',
    mode: 'same-origin',
    headers: headers,
    redirect: 'manual' // let browser handle redirects
} );
event.respondWith( fetch( req, {
        cache: 'no-store'
    } )
    .then( function ( response ) {
        return fetch( event.request )
    } )
    .catch( function ( err ) {
        return new Response( '<div><h2>Uh oh that did not work</h2></div>', {
            headers: {
                'Content-type': 'text/html'
            }
        } )
    } ) )
} );

The {cache:'no-store'} object as the second parameter to fetch , is an unfortunate NO-OP. Just doesn't work. Just keep it for the sake of a future scenario. It is really optional as of today.

If that worked, then you do not need to build a whole new Request object for fetch

cheers!

The code piece that creates a new request is generously borrowed from @pirxpilot 's answer here

The offline worker for this specific question on pastebin

https://pastebin./sNCutAw7

David, you have two errors in one line.

Your line

return catches.match(event.request.url + OFFLINE_URL);

should be

return caches.match('offline.html');

It's catches and you haven't defined OFFLINE_URL and you don't need event request url

I tried your code and I got the same result as you in the dev tools network tab. The network tab says it loaded the index.html from service-worker, but actually the service-worker returns the cached Offline Page as expected!

本文标签: pythonJavascriptService Workers not working correctlyStack Overflow