在没有捆绑程序的情况下将服务工作者更新为Workbox 5

时间:2020-06-02 08:35:30

标签: javascript progressive-web-apps workbox

我正在使用Workbox 3运行非常简单的PWA,主要用于缓存和脱机目的。该网页是用户可以安装PWA的论坛。计划升级是因为当前的工作箱3在测试时出现一些错误,因此我不得不重新构建服务人员。想给工作箱5一个机会。

下面的代码是我今天要测试的,目的应该是(非常简单):

  1. 为用户提供一个使用按钮“安装”新服务人员的机会(摘自:https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

  2. 缓存静态资产,但不缓存html(offline.html除外)。

  3. 提供导航预载了提高性能的机会。

  4. 基于Workbox 5创建服务工作者,该服务工作者将来很容易更新(推送消息)。

service-worker.js:

// Load WB locally, skip preload of googleapis in header. (Version 5.1.3)
importScripts('/workbox/workbox-sw.js');

workbox.setConfig({
  modulePathPrefix: '/workbox/'
});

// How are we doing?
if (workbox) {
  console.log('Workbox loaded correctly');
} else {
  console.log('Workbox did not load, check log');
}
/*
// Debug on or off, off in production
workbox.setConfig({
  debug: true
});
*/

// A new SW is waiting, user clicks button that activates the new SW
addEventListener('message', e => {
  if (e.data === 'skipWaiting') {
  skipWaiting();
  clientsClaim();
  }
});

// Cache Offline page
const CACHE_NAME = 'offline-html';
const FALLBACK_HTML_URL = '/offline.html';

addEventListener('install', async (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.add(FALLBACK_HTML_URL))
  );
});

// Start navigation preload to speed things up a bit.
workbox.navigationPreload.enable();

const networkOnly = new workbox.strategies.NetworkOnly();
const navigationHandler = async (params) => {
  try {
    // Attempt a network request.
    return await networkOnly.handle(params);
  } catch (error) {
    // If it fails, return the cached HTML and log the error
    console.log(error);
    return caches.match(FALLBACK_HTML_URL, {
      cacheName: CACHE_NAME,
    });
  }
};

// Register this strategy to handle all navigations.
const navigationRoute = new workbox.routing.NavigationRoute(navigationHandler);
workbox.routing.registerRoute(navigationRoute);

// Cache static assets
const {StaleWhileRevalidate} = workbox.strategies;
const {CacheFirst} = workbox.strategies;
const {CacheableResponsePlugin} = workbox.cacheableResponse;

  workbox.routing.registerRoute(
    ({request}) => request.destination === 'script' || request.destination === 'style' || request.destination === 'font' || request.destination === 'manifest',
      new StaleWhileRevalidate({
    // Use a custom cache name.
    cacheName: 'static-cache2',
  })
  );

// Cache image files.
workbox.routing.registerRoute(
  ({request}) => request.destination === 'image',
  // Use the cache if it's available.
  new CacheFirst({
    // Use a custom cache name.
    cacheName: 'image-cache2',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // Cache only 100 images.
        maxEntries: 100,
        // Cache for a maximum of two weeks.
        maxAgeSeconds: 14 * 24 * 60 * 60,
        purgeOnQuotaError: true,
      })
    ],
  })
);

// Try to cache opaque from CDN
workbox.routing.registerRoute(
  ({url}) => url.origin === 'https://cdn.mycdn.com' &&
             url.pathname.startsWith('/static/'),
  new CacheFirst({
    cacheName: 'cdn-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200],
      })
    ]
  })
);

客户端JS:

function showRefreshUI(registration) {
  // TODO: Display a toast or refresh UI.

  // This demo creates and injects a button.

  var button = document.createElement('button');
  button.style.position = 'absolute';
  button.style.bottom = '24px';
  button.style.left = '24px';
  button.textContent = 'A new version of the web app is waiting, click here to install';

  button.addEventListener('click', function() {
    if (!registration.waiting) {
      // Just to ensure registration.waiting is available before
      // calling postMessage()
      return;
    }

    button.disabled = true;

    registration.waiting.postMessage('skipWaiting');
  });

  document.body.appendChild(button);
};

function onNewServiceWorker(registration, callback) {
  if (registration.waiting) {
    // SW is waiting to activate. Can occur if multiple clients open and
    // one of the clients is refreshed.
    return callback();
  }

  function listenInstalledStateChange() {
    registration.installing.addEventListener('statechange', function(event) {
      if (event.target.state === 'installed') {
        // A new service worker is available, inform the user
        callback();
      }
    });
  };

  if (registration.installing) {
    return listenInstalledStateChange();
  }

  // We are currently controlled so a new SW may be found...
  // Add a listener in case a new SW is found,
  registration.addEventListener('updatefound', listenInstalledStateChange);
}

window.addEventListener('load', function() {
  var refreshing;
  // When the user asks to refresh the UI, we'll need to reload the window
  navigator.serviceWorker.addEventListener('controllerchange', function(event) {
    if (refreshing) return; // prevent infinite refresh loop when you use "Update on Reload"
    refreshing = true;
    console.log('Controller loaded');
    window.location.reload();
  });

  navigator.serviceWorker.register('/service-worker.js')
  .then(function (registration) {
      // Track updates to the Service Worker.
    if (!navigator.serviceWorker.controller) {
      // The window client isn't currently controlled so it's a new service
      // worker that will activate immediately
      return;
    }
    registration.update();

    onNewServiceWorker(registration, function() {
      showRefreshUI(registration);
    });
  });
});

此代码可以在我的开发服务器上工作,我对SO:s工作箱专家的问题是:它是否有任何陷阱,也许有人可以分享如何改进它的建议? >

Service Worker和Workbox很复杂,我主要担心的是,我已经构建了Service Worker,因此它无法针对Workbox进行优化,并且可能使用错误或错误的代码/功能/功能顺序。

0 个答案:

没有答案