Javascript 服务工作者:如何缓存第一个(动态)页面

Javascript 服务工作者:如何缓存第一个(动态)页面,javascript,progressive-web-apps,Javascript,Progressive Web Apps,我有一个一页的应用程序,它有一个动态URL,其中包含一个令牌,比如example.com/XV252GTH和各种资产,比如css、favicon等等 以下是我注册服务人员的方式: navigator.serviceWorker.register('sw.js'); 在所说的sw.js中,我在安装时预缓存资产: var cacheName = 'v1'; var cacheAssets = [ 'index.html', 'app.js', 'style.css',

我有一个一页的应用程序,它有一个动态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
(注册服务人员)页面只是一个模板,在发送到客户机之前在服务器上填充;所以在这个预缓存阶段,我只缓存模板,而不是页面

现在,在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;
        });
    });
}

但是,我如何才能确保它能够及时得到遵守,即:在软件安装完成后由客户端发送?在这种情况下,什么是好的做法?

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

应该清楚的是,您缓存了模板,因为您在站点生成之前缓存了模板,而且这种方法没有错,至少对于所有类型的文件都没有错。
例如,您的<代码> FavigIon。ICO < /代码>是一个文件,您可能会将其视为<代码>静态< /代码>。而且,它不会经常更改或根本不会更改,也不像您的
index.html
那样是动态的

因为有更新功能,所以在重新加载页面后为什么会有正确的版本也应该很清楚

这个问题的解决办法是回答您的问题:

如何将请求(
/XV252GTH
)从客户端(注册工作者的页面)发送到软件

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

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

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

    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;
          })
        })
      );
    });
    

    这些是解决你问题的基础。现在,您可以从中选择多种选项,使用相同的方法,但有一些附加功能。你选择哪一个取决于你自己,在不了解你的项目细节的情况下,没有人能告诉你选择哪一个。您也不限于一个选项。在某些情况下,您可能会将两个或多个选项组合在一起

    谷歌写了一篇关于所有选项的文章,并提供了所有选项的代码示例。他们还解释了你的想法。并不是每一个选项都对你来说都是有趣和相关的,但我建议你把它们全部读一遍,并彻底阅读

    这就是方法。

    好的,我从中得到了答案:要缓存在激活时注册工作者的页面,只需列出所有软件的客户端,并获取它们的(
    href
    属性)


    你不能使用浏览器历史api吗?我想提请大家注意我的答案,因为我相信这是一个更干净的解决方案,更普遍/最好的做法。你好,Aaron,你似乎是PWA方面的专家,我正在解决类似的问题,我可以设置可以缓存shell,但无法更新。你能看一下吗。谢谢你的夸奖。不幸的是,我不熟悉角。我曾经使用过其他前端框架,但这次没有。我不认为角度集成有什么问题,这部分工作得很好。我在升级过程中遇到的困难是我对
    SwUpdate
    的理解。如果你能看看
    PwaService
    的构造函数,告诉我是否有任何问题,那就足够了。好吧,至少我看不到任何问题。这可能就是你至今没有得到任何答案的原因。我不知道你对PWAs有多熟悉。如果您是新员工,您可能会忘记更新服务人员的步骤。注销您的服务人员,删除缓存(特别是从您的页面),启用“重新加载时更新”。。。否则,可能只是让旧的服务工作者使用旧的缓存运行。如果您更改服务人员的脚本,服务人员不会在默认情况下更新自己。谢谢Aaron。是的,我是PWA的新手,我不太了解注销、删除缓存、重新加载时更新。我想我没有做这些。让我看看这些。我的期望是更改
    ngsw config.json
    将触发
    此.swUpdate.available
    但即使
    此.swUpdate.isEnabled
    为真,它也不会触发。
    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('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;
          })
        })
      );
    });
    
    self.clients.matchAll({includeUncontrolled: true}).then(clients => {
        for (const client of clients) {
            updateCache(new URL(client.url).href);
        }
    });