Кэш обновлений Workbox в новой версии - PullRequest
1 голос
/ 29 марта 2020

Я реализовал Workbox для генерации моего сервисного работника с помощью веб-пакета. Это работает довольно хорошо - я могу подтвердить, что revision обновляется в сгенерированном сервисном работнике при запуске yarn run generate-sw (package. json: "generate-sw": "workbox inject:manifest").

Проблема в том, что я заметил, что мой клиенты не обновляют кеш после нового выпуска. Даже через несколько дней после обновления работника сервиса мои клиенты все еще кэшируют старый код, а новый код будет кэшироваться только после нескольких обновлений и / или отмены регистрации работника сервиса. Для каждого выпуска обновляется const CACHE_DYNAMIC_NAME = 'dynamic-v1.1.0'.

Как я могу гарантировать, что клиенты обновляют кэш сразу после нового выпуска?

serviceWorker-base. js

importScripts('workbox-sw.prod.v2.1.3.js')

const CACHE_DYNAMIC_NAME = 'dynamic-v1.1.0'
const workboxSW = new self.WorkboxSW()

// Cache then network for fonts
workboxSW.router.registerRoute(
  /.*(?:googleapis)\.com.*$/, 
  workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'google-font',
    cacheExpiration: {
      maxEntries: 1, 
      maxAgeSeconds: 60 * 60 * 24 * 28
    }
  })
)

// Cache then network for css
workboxSW.router.registerRoute(
  '/dist/main.css',
  workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'css'
  })
)

// Cache then network for avatars
workboxSW.router.registerRoute(
  '/img/avatars/:avatar-image', 
  workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'images-avatars'
  })
)

// Cache then network for images
workboxSW.router.registerRoute(
  '/img/:image', 
  workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'images'
  })
)

// Cache then network for icons
workboxSW.router.registerRoute(
  '/img/icons/:image', 
  workboxSW.strategies.staleWhileRevalidate({
    cacheName: 'images-icons'
  })
)

// Fallback page for html files
workboxSW.router.registerRoute(
  (routeData)=>{
    // routeData.url
    return (routeData.event.request.headers.get('accept').includes('text/html'))
  }, 
  (args) => {
    return caches.match(args.event.request)
    .then((response) => {
      if (response) {
        return response
      }else{
        return fetch(args.event.request)
        .then((res) => {
          return caches.open(CACHE_DYNAMIC_NAME)
          .then((cache) => {
            cache.put(args.event.request.url, res.clone())
            return res
          })
        })
        .catch((err) => {
          return caches.match('/offline.html')
          .then((res) => { return res })
        })
      }
    })
  }
)

workboxSW.precache([])

// Own vanilla service worker code
self.addEventListener('notificationclick', function (event){
  let notification = event.notification
  let action = event.action
  console.log(notification)

  if (action === 'confirm') {
    console.log('Confirm was chosen')
    notification.close()
  } else {
    const urlToOpen = new URL(notification.data.url, self.location.origin).href;

    const promiseChain = clients.matchAll({ type: 'window', includeUncontrolled: true })
    .then((windowClients) => {
      let matchingClient = null;
      let matchingUrl = false;
      for (let i=0; i < windowClients.length; i++){
        const windowClient = windowClients[i];

        if (windowClient.visibilityState === 'visible'){
          matchingClient = windowClient;
          matchingUrl = (windowClient.url === urlToOpen);
          break;
        }
      }

      if (matchingClient){
        if(!matchingUrl){ matchingClient.navigate(urlToOpen); }
        matchingClient.focus();
      } else {
        clients.openWindow(urlToOpen);
      }

      notification.close();
    });

    event.waitUntil(promiseChain);
  }
})

self.addEventListener('notificationclose', (event) => {
  // Great place to send back statistical data to figure out why user did not interact
  console.log('Notification was closed', event)
})

self.addEventListener('push', function (event){
  console.log('Push Notification received', event)

  // Default values
  const defaultData = {title: 'New!', content: 'Something new happened!', openUrl: '/'}
  const data = (event.data) ? JSON.parse(event.data.text()) : defaultData

  var options = {
    body: data.content,
    icon: '/images/icons/manifest-icon-512.png', 
    badge: '/images/icons/badge128.png', 
    data: {
      url: data.openUrl
    }
  }

  console.log('options', options)

  event.waitUntil(
    self.registration.showNotification(data.title, options)
  )
})

Должен ли я удалить кэш вручную или Workbox сделает это для меня?

caches.keys().then(cacheNames => {
  cacheNames.forEach(cacheName => {
    caches.delete(cacheName);
  });
});

С уважением / K

1 Ответ

2 голосов
/ 29 марта 2020

Я думаю, что ваша проблема связана с тем, что при обновлении приложения и его развертывании новый работник службы устанавливается, но не активируется. Что объясняет поведение, почему это происходит.

Причина этого в том, что registerRoute функция также регистрирует fetch прослушивателей, но эти прослушиватели выборки не будут вызываться до тех пор, пока не активируется новый работник службы. Кроме того, ответ на ваш вопрос: нет, вам не нужно удалять кеш самостоятельно. Workbox позаботится об этом.

Дайте мне знать больше деталей. Когда вы развертываете новый код и если пользователи закрывают все вкладки вашего сайта и открывают новую после этого, начинает ли он работать после 2 обновлений? Если так, то так и должно быть. Я обновлю свой ответ после того, как вы предоставите более подробную информацию.

Я бы посоветовал вам прочитать следующее: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68 и следовать третьему подходу.

...