使用 Service Worker 的渐进式 Web 应用程序:如何仅缓存页面的静态(非数据库相关)部分?

Progressive web app using Service Worker: How to cache only static (non database dependent) parts of a page?

我有一个需要离线工作的网络应用程序 (PHP/Laravel)。该应用程序用于管理我称之为 客户 的一些人。我的问题涉及一个页面,仅包含 table 列出我们所有客户的内容。路径是 /clients.

MySQL(在服务器上)和索引数据库(供离线使用)之间的同步工作正常,现在我必须缓存该页面的静态 HTML。我当前的方法缓存整个页面 ,包括来自服务器的客户端数据。但是,我不想缓存中 MySQL 的任何数据,因为当我离线时,我会从 Indexed DB 中获取所有数据。

这是我的简化版 offline-worker.js:

var CACHE_NAME = 'app-cache';

var urlsToCache = [
  '/clients'
];

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/offline-worker.js');
}

self.addEventListener('install', function(evt) {
  evt.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
    })
  )
});

self.addEventListener('fetch', function(evt) {
  evt.respondWith(
    caches.match(evt.request)
      .then(function(response) {

        if (response)
          return response;

        var fetchRequest = evt.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            if (!response || response.status != 200 || response.type != 'basic')
              return response;

            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(evt.request, responseToCache);
              });
            return response;
          }
        );
      })
  )
});

现在,我会在浏览器检测到数据离线时立即删除数据,但我宁愿从头开始缓存该页面的 'clean' version/template。这是 /clients:

的完全简化版本
<h1>Our clients</h1>
<table>
  <tr>
    <td>Charles</td>
    <td>Chaplin</td>
  </tr>
  <!-- Imagine many more rows of server generated data here. 
       I'd like to get rid of all rows before caching. -->
</table>

在没有来自 MySQL 的任何数据的情况下缓存 /clients 的最佳方法是什么?我知道浏览器看不到哪些页面元素来自服务器数据库,哪些是静态的。

如果我必须手动设置离线版本,那也没关系。我只需要知道如何在没有网络连接时告诉浏览器使用离线版本。

我会创建一个新的 url 路由 /clientsTemplate,您 return 只有模板。此模板存储在应用程序缓存中。

var CACHE_NAME = 'app-cache';

var urlsToCache = [
  '/clientsTemplate'
];

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/offline-worker.js');
}

self.addEventListener('install', function(evt) {
  evt.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
    })
  )
});

然后你可以按如下方式实现抓取:

self.addEventListener('fetch', function (event) {
  event.respondWith(
      fetch(event.request).then(function (response) {
        //the url concatenate is a bit hacky, works only if you don't have any url params
        return response || caches.match(event.request.url+"Template");
    })
  );
});

Stefs answer 显示了一个简单的解决方案,如果应用程序中的每个页面都有一个离线模式版本。到目前为止,我已经重写了获取事件的侦听器。这可能是仅缓存有限数量的离线模式页面的最简单方法。现在,只有一个这样的页面,/clients。一些其他页面和资产的 'standard' 版本也被缓存。

var CACHE_NAME = 'app-cache';

var urlsToCache = [
  '/offline/clients',
  '/css/bootstrap.min.css',
  'js/bootstrap.min.js',
  '/'
];

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/offline-worker.js');
}

// Open cache and add URLs to be cached
self.addEventListener('install', function(evt) {
  evt.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
    })
  )
});

// Define responses
self.addEventListener('fetch', function(event) {
  var request = event.request;

  event.respondWith(
    // Try to get page from server
    fetch(request).catch(function(error) {
      // Request failed: Get page from cache
      return caches.open('app-cache').then(function(cache) {
        // There's an offline mode version for this page
        if (request.url.indexOf('myapp.test/clients') > -1) {
          return cache.match('offline/clients');
        }
        // Standard version is used even in offline mode
        else {
          return cache.match(request.url);
        }
      });
    })
  );
});

我会通过 JS 渲染动态部分(组件)。每个 JS 部分将通过 Ajax 接收它需要的数据。那么平时的HTML和JS就可以缓存起来,以后离线使用。一旦用户离线,service worker 就可以开始从 IndexedDB 而不是后端返回数据。缓存的 HTML 和 JS 也将由 service worker 提供。