服务人员:如何缓存第一页(动态)

时间:2019-04-18 12:57:00

标签: javascript progressive-web-apps

我有一个页面应用程序,其中包含一个动态URL,该URL由令牌(如example.com/XV252GTH)和各种资产(如CSS,favicon等)构建。

这是我注册服务工作者的方式:

navigator.serviceWorker.register('sw.js');

sw.js中,我在安装时预缓存了资产:

var cacheName = 'v1';

var cacheAssets = [
    'index.html',
    'app.js',
    'style.css',
    'favicon.ico'
];

function precache() {
    return caches.open(cacheName).then(function (cache) {
        return cache.addAll(cacheAssets);
    });
}

self.addEventListener('install', function(event) {
    event.waitUntil(precache());
});

请注意,index.html(注册Service Worker)页面只是一个模板,在发送给客户端之前已在服务器上填充;因此,在此预缓存阶段,我仅缓存模板,而不缓存页面。

现在,在fetch事件中,所有不在缓存中的请求资源都将复制到其中:

addEventListener('fetch', event => {
    event.respondWith(async function() {
        const cachedResponse = await caches.match(event.request);
        if (cachedResponse) return cachedResponse;       
        return fetch(event.request).then(updateCache(event.request));
    }());
});

使用此更新功能

function updateCache(request) {
    return caches.open(cacheName).then(cache => {
        return fetch(request).then(response => {
            const resClone = response.clone();
            if (response.status < 400)
                return cache.put(request, resClone);
            return response;
        });
    });
}

在此阶段,所有资产都在高速缓存中,而不是动态生成的页面中。仅在重新加载后,我才能在缓存中看到另一个条目:/XV252GTH现在,该应用可离线使用;但是页面的这种重新加载无法达到整个Service Worker的目的。

问题:如何从客户端(注册工作人员的页面)向SW发送请求(/XV252GTH)?我想我可以在sw.js

中设置一个侦听器
self.addEventListener('message', function(event){
    updateCache(event.request)
});

但是我如何确定它会及时兑现,即:在软件完成安装后由客户端发送?在这种情况下有什么好的做法?

2 个答案:

答案 0 :(得分:3)

如果我理解不对,请纠正我!
您可以在此处预缓存文件:

var cacheAssets = [
    'index.html',
    'app.js',
    'style.css',
    'favicon.ico'
];

function precache() {
    return caches.open(cacheName).then(function (cache) {
        return cache.addAll(cacheAssets);
    });
}

很明显,您缓存了模板,因为您在构建网站之前就缓存了模板,这种方法是正确的,至少不是对所有类型的文件都是如此。
例如,您的favicon.ico是一个文件,您可能会认为它是static。另外,它不会经常更改或根本不会更改,并且它不像您的index.html那样动态。
Schema Source

还应该清楚的是,由于具有更新功能,为什么在重新加载页面后仍具有正确的版本。

此问题的解决方案是您的问题的答案:

  

如何将客户端(注册工作人员的页面)上的请求(/XV252GTH)发送到SW?

如果后端构建了您的网页,则要缓存它,而不是在安装service-worker之前缓存它。所以这是它的工作方式:

  1. 您有一个空的缓存,或者至少有一个没有index.html的缓存。
  2. 通常,将向服务器发送请求以获取index.html。取而代之的是,我们向缓存发出请求,并检查index.html是否在缓存中,至少您是第一次加载该页面。
  3. 由于缓存中没有匹配项,请向服务器发出请求以将其获取。如果页面正常加载页面,则该请求与页面执行的请求相同。因此,服务器将构建您的index.html并将其发送回页面。
  4. 收到index.html后,将其加载到页面并将其存储在缓存中。

示例方法为Stale-while-revalidateenter image description here

  

如果有可用的缓存版本,请使用它,但下次获取更新。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function(cache) {
      return cache.match(event.request).then(function(response) {
        var fetchPromise = fetch(event.request).then(function(networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        })
        return response || fetchPromise;
      })
    })
  );
});

Source

这些是解决问题的基础。现在,您可以使用相同的方法从各种各样的选项中进行选择,但是还具有一些其他功能。您选择哪一个取决于您,并且在不了解您的详细项目的情况下,没人会告诉您选择哪个。您也不限于一种选择。在某些情况下,您可能将两个或多个选项组合在一起。

Google为您提供的所有选项写了一个很棒的guide,并提供了所有示例的代码示例。他们还解释了您的current version。并非每个选项都会对您有意义且有意义,但我建议您阅读所有内容并仔细阅读。

这是要走的路。

答案 1 :(得分:0)

好的,我从this page得到了答案:要缓存在激活时注册工作程序的页面,只需列出所有SW的客户端,并获取它们的URLhref属性)。

self.clients.matchAll({includeUncontrolled: true}).then(clients => {
    for (const client of clients) {
        updateCache(new URL(client.url).href);
    }
});