服务人员-使用skipWaiting()更新新版本的缓存 [英] Service worker - update cache on new version using skipWaiting()

查看:102
本文介绍了服务人员-使用skipWaiting()更新新版本的缓存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了Workbox,以使用webpack生成服务工作者。可行-我可以在运行 generate-sw: workbox inject:manifest 时确认生成的服务工作程序中的修订已更新。

I have implemented Workbox to generate my service worker using webpack. This works - I can confirm revision is updated in the generated service worker when running "generate-sw": "workbox inject:manifest".

问题是-我注意到我的客户端在新版本发布后没有更新缓存。即使在更新服务人员后几天,我的客户仍在缓存旧代码,并且只有在几次刷新和/或注销服务人员之后,新代码才会加载。对于每个发行版,我都确认修订已更新。

The problem is - I have noticed my clients are not updating the cache after a new release. Even days after updating the service worker my clients are still caching the old code and new code will only load after several refreshes and/or unregistering the service worker. For each release I have confirmed that the revision is updated.

我了解我需要实施 skipWaiting 以确保客户端得到更新-尤其是PWA。我已阅读并尝试遵循此处的第三种方法: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68

I understand that I need to implement skipWaiting to ensure the clients gets updated - especially PWA. I have read, and tried to follow the 3rd approach here: https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68.

我的应用安装在 app.js

我已将此代码添加到
serviceWorker-base.js

addEventListener('message', function(messageEvent){
  if (messageEvent.data === 'skipWaiting') return skipWaiting();
});

我在 app.js 中有此代码

const runServiceWorker = true
const serviceWorkerAvailable = ('serviceWorker' in navigator) ? true : false

// reload once when the new Service Worker starts activating
let refreshing
navigator.serviceWorker.addEventListener('controllerchange', function() {
    if (refreshing) return
    refreshing = true
    window.location.reload()
  }
)

function promptUserToRefresh(reg) {
  // this is just an example - don't use window.confirm in real life; it's terrible
  if (window.confirm("New version available! OK to refresh?")) {
    reg.waiting.postMessage('skipWaiting')
  }
}

function listenForWaitingServiceWorker(reg, callback) {
  console.log('listenForWaitingServiceWorker')
  function awaitStateChange() {
    reg.installing.addEventListener('statechange', function() {
      if (this.state === 'installed') callback(reg)
    })
  }
  if (!reg) return
  if (reg.waiting) return callback(reg)
  if (reg.installing) awaitStateChange()
  reg.addEventListener('updatefound', awaitStateChange)
}

// Register service worker
if (runServiceWorker && serviceWorkerAvailable) {
  navigator.serviceWorker.register('/serviceWorker.js')
  .then( (registration) => {
    console.log('Service worker registered', registration)
    listenForWaitingServiceWorker(registration, promptUserToRefresh) // <-- Added to existing code
  })
}else{
  console.log('Service worker disabled - process.env.NODE_ENV', process.env.NODE_ENV)
}

此代码的问题是 promptUserToRefresh()仅被调用

The problem with this code is that promptUserToRefresh() only gets called on initial service worker install, not when a new service worker is waiting!

此外,在接受首次安装时,在接受第一次安装时也会出现以下错误。

Also, I get the below error when accepting the first install.

TypeError: registration.waiting is null
promptUserToRefresh app.js:154
awaitStateChange app.js:162

该错误在
promptUserToRefresh(registration)中触发 registration.waiting.postMessage('skipWaiting')

我也用相同的结果测试了这种方法:< a href = https://github.com/GoogleChrome/workbox/issues/1120 rel = nofollow noreferrer> https://github.com/GoogleChrome/workbox/issues/1120

I have also tested this approach with the same result: https://github.com/GoogleChrome/workbox/issues/1120

推荐答案

代码现在在船尾

更新了 app.js

// *** PWA Functionality START ***

// skipWaiting() functions
function promptUserToRefresh(registration) {
  // this is just an example - don't use window.confirm in real life; it's terrible
  if (window.confirm("New version available! Refresh?")) {
    registration.waiting.postMessage('skipWaiting')
  }
}
function listenForWaitingServiceWorker(registration) {
  console.log('listenForWaitingServiceWorker', registration)
  function awaitStateChange() {
    registration.installing.addEventListener('statechange', function() {
      if (this.state === 'installed') promptUserToRefresh(registration)
    })
  }
  if (!registration) return
  if (registration.waiting) return promptUserToRefresh(registration)
  if (registration.installing) awaitStateChange()
  registration.addEventListener('updatefound', awaitStateChange)
}
//**

const enableServiceWorker = true
const serviceWorkerAvailable = ('serviceWorker' in navigator) ? true : false
// Register service worker
if (enableServiceWorker && serviceWorkerAvailable) {
  navigator.serviceWorker.register('/serviceWorker.js')
  .then( (registration) => {
    console.log('Service worker registered', registration)
    listenForWaitingServiceWorker(registration) // ** skipWaiting() code
  })
}else{
  console.log('Service worker disabled - process.env.NODE_ENV', process.env.NODE_ENV)
}

// Install prompt event handler
export let deferredPrompt
window.addEventListener('beforeinstallprompt', (event) => {
  // Prevent Chrome 76 and later from showing the mini-infobar
  event.preventDefault()
  deferredPrompt = event // Stash the event so it can be triggered later.
  try{
    showInstallPromotion()
  }catch(e){
    console.error('showInstallPromotion()', e)
  }
})
window.addEventListener('appinstalled', (event) => {
  console.log('a2hs installed')
})
// *** PWA Functionality END *

也许下面的(删除的)行引起了所有麻烦?

Maybe the below (removed) lines caused all the trouble?

// reload once when the new Service Worker starts activating
let refreshing
navigator.serviceWorker.addEventListener('controllerchange', function() {
    if (refreshing) return
    refreshing = true
    window.location.reload()
  }
)

现在剩下的就是弄清楚在首次访问该应用/安装时显示提示! (^ __ ^)/

All that remains now is figuring out how not to show the prompt on first visit to the app / install! (^__^)/

这篇关于服务人员-使用skipWaiting()更新新版本的缓存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆