【问题标题】:Service Worker: How to cache the first (dynamic) pageService Worker:如何缓存第一个(动态)页面
【发布时间】:2019-04-18 12:57:00
【问题描述】:

我有这个单页应用程序,它带有一个使用令牌(如 example.com/XV252GTH)和各种资产(如 css、favicon 等)构建的动态 URL。

这是我注册 Service Worker 的方法:

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 的目的。

问题:如何从客户端(注册worker的页面)向SW发送请求(/XV252GTH)?我想我可以在sw.js中设置一个监听器

self.addEventListener('message', function(event){
    updateCache(event.request)
});

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

【问题讨论】:

  • 不能用浏览器历史API吗?

标签: javascript progressive-web-apps


【解决方案1】:

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

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

【讨论】:

  • 我想提请注意我的回答,因为我相信这是一个更清洁的解决方案并且更普遍/最佳实践。
【解决方案2】:

如果我理解错了,请纠正我!
你在这里预缓存你的文件:

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 那样动态。
Source

既然你有更新功能,你也应该清楚为什么在重新加载页面后你的版本是正确的。

这个问题的解决方案就是你问题的答案:

如何从客户端(注册worker的页面)向SW发送请求(/XV252GTH)?

如果后端构建了您的网页,您希望在安装 service-worker 之前缓存它,而不是缓存它。下面是它的工作原理:

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

一个示例方法是Stale-while-revalidate

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

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。并非每个选项都会对您感兴趣且相关,但我建议您通读并彻底阅读它们。

这是要走的路。

【讨论】:

  • 嗨 Aaron,你似乎是 PWA 方面的专家,我也遇到了类似的问题,我可以设置可以缓存 shell 但不能更新。你能快速看一下吗? stackoverflow.com/questions/55797611/…
  • 感谢您的夸奖。不幸的是,我不熟悉 Angular。我使用过其他前端框架,但不是这个。
  • 我认为角度整合没有问题,那部分工作正常。我正在努力升级它,问题在于我对 SwUpdate 的理解。如果你可以看看PwaService的构造函数,如果有什么问题告诉我就足够了。
  • 好吧,至少我什么都看不到。这可能是您至今没有收到任何答复的原因。我不知道你对 PWA 有多熟悉。如果您是相对较新的人,您可能忘记了更新服务人员的步骤。取消注册您的服务工作者,删除缓存(特别是从您的页面中),启用“重新加载时更新”......否则,您可能只是让旧服务工作者使用旧缓存运行。如果您更改服务工作人员的脚本,服务工作人员默认不会更新自己。
  • 谢谢 Aaron。是的,我是 PWA 的新手,我不太了解取消注册、删除缓存、重新加载时更新。我不认为我做过这些。让我读一下这些。我的期望是改变ngsw-config.json 会触发this.swUpdate.available 但即使this.swUpdate.isEnabled 是真的也不会触发。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多