简体   繁体   English

Javascript-服务人员无法正常工作

[英]Javascript - Service Workers not working correctly

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. 目前,我将offline.html保存到缓存中,以便浏览器可以在没有内部连接的情况下显示此文件。

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. 在服务工作者的fetch事件中,我尝试加载index.html ,如果失败(没有互联网连接),我将从缓存加载offline.html

However, whenever I check offline mode in developer tools and refresh the page index.html still shows... 但是,每当我在开发人员工具中检查离线模式并刷新页面时, index.html仍显示...

The request isn't failing, and it looks like index.html is being cached even though I didn't specify it to be. 该请求没有失败,即使我未指定为index.html ,它也似乎正在缓存。

Here is my HTML for index.html : 这是我的index.html 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 : 这是我的offline.html HTML:

<!DOCTYPE html>
<html>
<head>
    <title>You are Offline - Service Workers - Test</title>
</head>
<body>
    <h1> Welcome 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 : 这是我的service-worker.js javascript:

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的简单http服务器运行服务器,如下所示:

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. 这些图像显示index.html (localhost)仍在没有互联网的情况下加载,这意味着必须对其进行缓存。

脱机复选框已打勾

本地主机状态:200(来自ServiceWorker)| service-worker.js状态:失败

Edit 2: 编辑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. 我试图将no-cache添加到index.html的获取中,但当我进行离线检查时,它仍在获取index.html

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

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 catches ,您还没有定义OFFLINE_URL ,也不需要事件请求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! 网络选项卡说它从service-worker加载了index.html,但是实际上service-worker返回了缓存的脱机页面!

显示离线页面

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. 这里的问题是,当服务工作者拦截请求时,将从磁盘缓存中提供index.html browser ===> Service Worker ===> fetch event 浏览器 ===> 服务工作者 ===> 获取事件

inside the fetch event, we have , 在fetch事件中,我们有

  • 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? Service Worker OnFetch ===> 检入磁盘缓存 ===> 什么都没有? Fetch Online 在线获取

The page being fetched here, is index.html 此处获取的页面是index.html

and the cache-control headers for index.html , 以及index.htmlcache-control标头,

Do Not Specify a no-cache 指定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 index.html设置具有限制值的cache-control标头-在服务器端
  • 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 comes to a GET 显然,当涉及到GET时,提取和浏览器对请求正文有自己的保留

Also, weirdness and utter chaos happens If you reuse the event.request object, for a fetch request, and add custom headers. 同样,如果您重复使用event.request对象以获取请求并添加自定义标头,则会发生怪异和完全混乱。 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. 由于fetch事件的request.mode属性,混乱是Uncaught Exceptions的列表,该列表禁止您在无条件或导航模式下将自定义标头添加到fetch。

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. 检查是否可以获取虚拟的html页面,在源下面说test-connectivity.html ,并带有一个自定义cache: no-cache标头。 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 . {cache:'no-store'}对象是要fetch的第二个参数,它是不幸的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 如果可行,那么您无需构建一个全新的Request对象即可进行fetch

cheers! 干杯!

The code piece that creates a new request is generously borrowed from @pirxpilot 's answer here 创建一个新的请求的代码片慷慨从@pirxpilot的回答借来这里

The offline worker for this specific question on pastebin 在pastebin上针对此特定问题的离线工作者

https://pastebin.com/sNCutAw7 https://pastebin.com/sNCutAw7

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM