当前位置: 首页 > 知识库问答 >
问题:

新版本上的Workbox更新缓存

费子濯
2023-03-14

我已经实现了Workbox,以使用webpack生成我的服务人员。这非常有效-我可以确认,在运行warn run generate sw(package.json:“generate sw”:“workbox inject:manifest”)时,已在生成的服务工作人员中更新了版本。

问题是-我注意到我的客户在新版本发布后没有更新缓存。即使在更新service worker几天后,我的客户端仍在缓存旧代码,而新代码只有在多次刷新和/或注销service worker后才会缓存。对于每个版本,const CACHE\u DYNAMIC\u NAME='DYNAMIC-v1。1.0'已更新。

如何确保客户端在新版本发布后立即更新缓存?

服务人员基地。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


共有2个答案

郎飞航
2023-03-14

当文件位于本地而不是CDN时,让WorkBox更新的一种方法是以下方法:

>

  • 在您的serviceworker.js文件中添加一个事件监听器,以便WorkBox在有更新时跳过等待,我的代码如下:

     importScripts('Scripts/workbox/workbox-sw.js');
     if (workbox) {
    
         console.log('Workbox is loaded :)');
    
         // Add a message listener to the waiting service worker
         // instructing it to skip waiting on when updates are done. 
         addEventListener('message', (event) => {
             if (event.data && event.data.type === 'SKIP_WAITING') {
                 skipWaiting();
             }
         });
         // Since I am using Local Workbox Files Instead of CDN I need to set the modulePathPrefix as follows
         workbox.setConfig({ modulePathPrefix: 'Scripts/workbox/' });
    
         // other workbox settings ...
     }
    

    如果服务辅助角色位于导航器中,则在客户端页面中为加载添加事件侦听器。作为一个注释,我在MVC中这样做,所以我把我的代码放在_Layout.cshtml,这样它就可以从我网站上的任何页面更新。

     <script type="text/javascript">
         if ('serviceWorker' in navigator) {
             // Use the window load event to keep the page load performant
             window.addEventListener('load', () => {
                 navigator.serviceWorker
                     // register WorkBox, our ServiceWorker.
                     .register("<PATH_TO_YOUR_SERVICE_WORKER/serviceworker.js")", { scope: '/<SOME_SCOPE>/' })
                     .then(function (registration) {
                         /**
                          * Wether WorkBox cached files are being updated.
                          * @type {boolean}
                          * */
                         let updating;
    
                         // Function handler for the ServiceWorker updates.
                         registration.onupdatefound = () => {
                             const serviceWorker = registration.installing;
                             if (serviceWorker == null) { // service worker is not available return.
                                 return;
                             }
    
                             // Listen to the browser's service worker state changes
                             serviceWorker.onstatechange = () => {
                                 // IF ServiceWorker has been installed 
                                 // AND we have a controller, meaning that the old chached files got deleted and new files cached
                                 // AND ServiceWorkerRegistration is waiting
                                 // THEN let ServieWorker know that it can skip waiting. 
                                 if (serviceWorker.state === 'installed' && navigator.serviceWorker.controller && registration && registration.waiting) {
                                     updating = true;
                                     // In my "~/serviceworker.js" file there is an event listener that got added to listen to the post message.
                                     registration.waiting.postMessage({ type: 'SKIP_WAITING' });
                                 }
    
                                 // IF we had an update of the cache files and we are done activating the ServiceWorker service
                                 // THEN let the user know that we updated the files and we are reloading the website. 
                                 if (updating && serviceWorker.state === 'activated') {
                                     // I am using an alert as an example, in my code I use a custom dialog that has an overlay so that the user can't do anything besides clicking okay.
                                     alert('The cached files have been updated, the browser will re-load.');
                                     window.location.reload();
                                 }
                             };
                         };
    
                         console.log('ServiceWorker registration successful with scope: ', registration.scope);
                     }).catch(function (err) {
                         //registration failed :(
                         console.log('ServiceWorker registration failed: ', err);
                     });
             });
         } else {
             console.log('No service-worker on this browser');
         }
     </script>
    

    注意:我使用浏览器的serviceWorker来更新我的WorkBox缓存文件,而且,我只在Chrome测试过,我没有在其他浏览器中尝试过。

  • 慕河
    2023-03-14

    我认为您的问题与这样一个事实有关,即当您更新应用并部署时,会安装新的服务工作人员,但不会激活。这解释了为什么会发生这种情况。

    原因是registerRoute函数也注册fetch侦听器,但在新的服务工作人员启动之前,不会调用这些fetch侦听器。另外,您的问题的答案是:不,您不需要自己删除缓存。Workbox负责这些。

    让我知道更多细节。当您部署新代码时,如果用户关闭网站的所有选项卡,然后打开一个新的选项卡,它是否会在刷新2次后开始工作?如果是这样的话,它应该是这样工作的。在你提供更多细节后,我会更新我的答案。

    我建议你阅读以下内容:https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68并遵循第三种方法。

     类似资料:
    • 我已经安装了R 3.3.3,现在尝试将其更新到3.4.1版本。使用了以下说明: sudo-H gedit/etc/apt/sources . list deb http://cran.rstudio.com/bin/linux/ubuntu precise/sudo apt-get更新sudo apt-get安装r-base 毕竟,安装的版本仍然是R3.3.3。 还尝试从usr/local/bin

    • v2.0.9[2020-4-12] 修复(Fixed): 修复 MySQL 8.0 生成实体主键位置错误 e437d36 更新(Update): 连接池关闭连接, 如果连接已经断开, 异常将会被忽略 7aac80da 增强(Enhancement): v2.0.8[2020-1-18] 修复(Fixed): 修复 ws server 的 message response.finish 兼容 swo

    • 网防G01最新版本为:Linux版本:3.0.63.10,Windows版本:3.1.18.6,PC客户端版本:3.1.18。 更新记录: 2020年03月30日 2018年11月06日 2018年05月15日 2017年10月10日 2017年07月21日 2020年3月30日,更新说明 Linux版本:3.1.20.15 Windows版本:3.1.20.15 PC客户端版本:3.1.20.1

    • 2018-06-19:更新 Homestead 版本到 v7.8.0; 2018-06-17:更新 Homestead 等虚拟机软件到最新;

    • 现在创建一个 git版本库:(参见“初始化”一节) mkdir sandbox cd sandbox/ git init touch test git add . git commit -m "创建git版本库" git log查看版本纪录: commit d63e709f565dcd60ab749f0eca27a947b02b8c26 Author: kardinal <2999am@g

    • 问题内容: 我在安装项目时遇到了一些问题。 我拥有运行 Swift 3* 的最新版本的 Xcode ,并且当我尝试安装alamofire时遇到800个编译器错误。 * 显然地 构建Alamofire 4.0.0+需要CocoaPods 1.1.0+ 我看着终端机上的CocoaPods版本,它说我的版本是1.0.1。 我猜运行更新没有用,因为CocoaPods 1.1是beta版。 因此,我不确定如