【问题标题】:Progressive Web App and caching UI渐进式 Web 应用程序和缓存 UI
【发布时间】:2017-08-16 14:59:46
【问题描述】:

我正在开发一个具有所有功能(如离线、添加到主屏幕、通知等)的 PWA,但是当我尝试刷新 UI 时遇到了一些问题。

换句话说,我的 PWA 具有在 index.html 文件中定义的 UI,我想做的是缓存 PWA 以供离线使用(我可以这样做),如果设备在线检查是否有一些 UI 更新(在 index.html 文件中,或在它依赖的文件中),下载此更改并刷新设备上和缓存内的 UI。

有了数据,我对缓存没有任何问题。

例如如果我有这个页面index.html:

<!DOCTYPE html>
<html>
<head >
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title class="title">PWA</title>

  <link rel="manifest" href="manifest.json">

  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="PWA">
  <link rel="apple-touch-icon" href="imgs/icons/Icon-152.png">
  <meta name="msapplication-TileImage" content="imgs/icons/Icon-144.png">
  <meta name="msapplication-TileColor" content="#2F3BA2">
  <link rel="shortcut icon" sizes="32x32" href="imgs/icons/Icon-32.png">
  <link rel="shortcut icon" sizes="196x196" href="imgs/icons/Icon-196.png">
  <link rel="apple-touch-icon-precomposed" href="imgs/icons/Icon-152.png">
  </head>

<body>

<div class="container">
    <div class="row">
        <div class="col-12">
            <img src="imgs/images.png">
        </div>
    </div>
</div>

 <script src="js/jquery.js"></script>
 <script src="js/bootstrap.js"></script>
 <script src="js/app.js"></script>
</body>
</html>

app.js:

  if('serviceWorker' in navigator) {
    navigator.serviceWorker
             .register('service-worker.js')
             .then(function() { console.log('Service Worker Registered'); });
  }

service-worker.js:

var dataCacheName = 'dataCache-v2';
var cacheName = 'cache-v2';
var filesToCache = [
  'index.html',
  'imgs/images.png',
    'src/app.js'
];

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching App Shell');
      return cache.addAll(filesToCache);
    })
  );
});

self.addEventListener('activate', function(e) {
  console.log('[ServiceWorker] Activate');
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        console.log('[ServiceWorker] Removing old cache', key);
        if (key !== cacheName && key !== dataCacheName) {
          return caches.delete(key);
        }
      }));
    })
  );
return self.clients.claim();
});

self.addEventListener('fetch', function(e) {
  console.log('[ServiceWorker] Fetch', e.request.url);
  var dataUrl = 'URL-WHERE-FIND-DATA';
  if (e.request.url.indexOf(dataUrl) === 0) {
    e.respondWith(
      fetch(e.request)
        .then(function(response) {
          return caches.open(dataCacheName).then(function(cache) {
            cache.put(e.request.url, response.clone());
            console.log('[ServiceWorker] Fetched&Cached Data');
            return response;
          });
        })
    );
  } else {
    e.respondWith(
      caches.match(e.request).then(function(response) {
        return response || fetch(e.request);
      })
    );
  }
});

假设我将images.png 替换为另一个图像但名称相同,我如何向用户显示新图像? 如果我刷新页面,数据是从网络获取的(如果可用),但图像仍然从缓存中捕获。

我希望我已经很好地解释了我的问题。非常感谢所有帮助我的人

更新 #1:

我尝试按照 Arnelle Balane 对我的建议实施“先缓存后网络”策略,但问题始终存在,浏览器始终显示缓存的图像(在下面的代码中,我尝试更新名为“demo.xml”的图像) .jpg')。 可能我做错了什么。 这是我的代码:

service-worker.js:

self.addEventListener('fetch', function(event) {
      event.respondWith(
    caches.open("my-cache").then(function(cache) {
      return fetch(event.request).then(function(response) {
          console.log('Fetch: ' + response.url);
        cache.put(event.request, response.clone());
        return response;
      });
    })
  );
});

app.js:

var networkDataReceived = false;


var networkUpdate = fetch('https://website.com/app/images/demo.jpg').then(function(response) {
  return response.blob();
}).then(function(data) {
  networkDataReceived = true;
  updatePage(data);
});

caches.match('https://website.com/app/images/demo.jpg').then(function(response) {
  if (!response) throw Error("No data");
  return response.blob();
}).then(function(data) {
  if (!networkDataReceived) {
    updatePage(data);
  }
}).catch(function() {
  return networkUpdate;
}).catch(showErrorMessage);


function showErrorMessage(response){
    console.log("Error: " + response);
}

function updatePage(response) {
  var img = document.getElementById('demo');
  var imageUrl = URL.createObjectURL(response);
  img.src = imageUrl;
}

有什么新建议吗?谢谢

更新 #2:

现在我正在尝试从头开始做所有事情。 我已经从这个谷歌的例子中复制了服务工作者:https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/ 这实现了“缓存然后网络”策略。 service-worker.js的代码是这样的:

var dataCacheName = 'dataCache1';
var cacheName = 'cache1';

var filesToCache = [
    '/',
    'index.html',
    'css/main.css',
    'src/app.js'
];

self.addEventListener('install', function(e) {
    console.log('[ServiceWorker] Install');
    e.waitUntil(
        caches.open(cacheName).then(function(cache) {
            console.log('[ServiceWorker] Caching app shell');
            return cache.addAll(filesToCache);
        })
    );
});

self.addEventListener('activate', function(e) {
    console.log('[ServiceWorker] Activate');
    e.waitUntil(
        caches.keys().then(function(keyList) {
            return Promise.all(keyList.map(function(key) {
                if (key !== cacheName && key !== dataCacheName) {
                    console.log('[ServiceWorker] Removing old cache', key);
                    return caches.delete(key);
                }
            }));
        })
    );
    /*
     * Fixes a corner case in which the app wasn't returning the latest data.
     * You can reproduce the corner case by commenting out the line below and
     * then doing the following steps: 1) load app for first time so that the
     * initial New York City data is shown 2) press the refresh button on the
     * app 3) go offline 4) reload the app. You expect to see the newer NYC
     * data, but you actually see the initial data. This happens because the
     * service worker is not yet activated. The code below essentially lets
     * you activate the service worker faster.
     */
    return self.clients.claim();
});

self.addEventListener('fetch', function(e) {
    console.log('[Service Worker] Fetch', e.request.url);
    var dataUrl = 'https:/mywebsite.it/service/images/demo.jpg';
    if (e.request.url.indexOf(dataUrl) > -1) {
        /*
         * When the request URL contains dataUrl, the app is asking for fresh
         * weather data. In this case, the service worker always goes to the
         * network and then caches the response. This is called the "Cache then
         * network" strategy:
         * https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
         */
        e.respondWith(
            caches.open(dataCacheName).then(function(cache) {
                return fetch(e.request).then(function(response){
                    cache.put(e.request.url, response.clone());
                    return response;
                });
            })
        );
    } else {
        /*
         * The app is asking for app shell files. In this scenario the app uses the
         * "Cache, falling back to the network" offline strategy:
         * https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
         */
        e.respondWith(
            caches.match(e.request).then(function(response) {
                return response || fetch(e.request);
            })
        );
    }
});

结果总是一样的,图像不会更新。 使用 fetch(url) 方法,服务工作者应该从网络中获取图像,对吧? 代码结果如下图所示。

当我重新加载页面时,唯一获取的请求是文件夹“/service/”

如果我尝试通过对浏览器 (https://mywebsite.com/service/images/demo.jpg) 的显式请求来“强制”加载我想要更新的图像,服务工作者会正确获取请求,但始终显示旧图像。 我认为我在做一些愚蠢的错误,但我不明白是什么。

【问题讨论】:

  • 有几种方法可以处理静态文件缓存。您可以在您的网络服务器(nginx)或 webpack(javascript 应用程序的模块捆绑器)中处理它
  • 您认为哪种方法最好?现在我看到了 sw-precache。也许它可以做我想做的事,因为它对每个文件进行哈希处理以捕获新版本(如果存在)(对吗?)
  • 我在我的项目中使用了 react js,它与 webpack 配合得很好。 Webpack 有一个称为“加载器”的概念,它为您预处理静态文件。您可以查看文档以更好地理解它。 webpack.js.org/loaders/html-loader

标签: javascript caching web-applications service-worker progressive-web-apps


【解决方案1】:

从缓存中提供图像的原因是因为这就是服务工作者的编码:

e.respondWith(
  caches.match(e.request).then(function(response) {
    return response || fetch(e.request);
  })
);

如果请求的响应已经存储,这将首先检查您的缓存,如果没有,则仅从网络中获取资源。

在我看来,您有两种可能的处理方式:

  1. 确保资源(包括图像)的文件名在资源的实际内容发生变化时发生变化。有几个构建工具可以通过将资源的哈希附加到其文件名来为您完成此操作。资源内容的更改将导致不同的哈希值,从而导致不同的文件名。

  2. 您可以使用 Cache then network 策略,如 Jake Archibald 的 this article 中所述。这个想法是,如果缓存的资源可用,您将提供它,同时您通过网络请求该资源。网络请求完成后,您将使用从网络获得的内容替换先前提供的内容,并更新资源的缓存版本。通过这种方式,您可以确保用户看到的是资源的最新版本,同时通过更新缓存版本的资源仍然不会破坏离线体验。

【讨论】:

  • 我已经阅读了您在第二种方法(缓存然后网络)中向我建议的文章,但图像的问题仍然存在。我刚刚用我的问题更新了我的问题
猜你喜欢
  • 2019-04-04
  • 1970-01-01
  • 2020-10-04
  • 2018-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
相关资源
最近更新 更多