服务人员-使用skipWaiting()更新新版本的缓存 [英] Service worker - update cache on new version using 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屋!