在 Vue PWA 中处理 Service Worker 更新
更新 Service Worker 注册
制作更新混合
更新我们的 UI
跳过服务工作等待
更新我们的服务工作者
重新加载页面
TL;DR
目录
Vue.js 太棒了!它简单易用,极其灵活,还拥有一些很棒的 DevTools。既然你正在读这篇文章,我就假设你已经了解这些了。
Vue CLI 就是这样一款 DevTool,它允许我们快速轻松地向 Vue 应用添加插件。鉴于构建 PWA 的趋势日益增长和普及,Vue CLI 拥有自己的 PWA 插件也就不足为奇了,而且总的来说,它的表现正如你所期望的那样出色。
如果您只是想为您的网站添加一些基本的 PWA 功能,那么它绝对plugin-pwa
是个好东西。只需安装它,即可立即获得用于安装的清单和用于预缓存的服务工作线程。如果您想添加主题颜色、更改 PWA 名称等,它甚至还提供了大量的配置选项。
它没有提供在发现更新的 Service Worker 时激活 Service Worker 的功能。所以我们自己添加吧。
更新 Service Worker 注册
安装时,plugin-pwa
它会添加一个包含一些基本配置和事件的registerServiceWorker.js
文件src
。有关此文件的更多信息,请随时在 npm 上查看register-service-worker。对于本教程来说,我们唯一需要的部分就是update()
函数。在全新安装的情况下,它如下所示:
updated () {
console.log('New content is available; please refresh.')
}
我们需要稍微修改一下这个函数,让它在有更新时不仅仅记录到我们的控制台上。
首先,我们需要访问刚刚注册的 Service Worker。幸运的是,Service Workerregister-service-worker
帮我们搞定了这个问题。根据他们的文档:
、、和事件在
ready
其参数中传递一个ServiceWorkerRegistrationregistered
实例。cached
updatefound
updated
完美!只需将ServiceWorkerRegistration
in 作为参数传递,我们就可以开始比赛了。接下来我们要面对的问题是将这些注册详细信息发送到我们的 Vue 应用。因此,我们可以使用CustomEvent来处理这个问题。现在我们的update()
函数应该如下所示:
updated(registration) {
console.log('New content is available; please refresh.')
document.dispatchEvent(
new CustomEvent('swUpdated', { detail: registration })
)
}
我们现在传入ServiceWorkerRegistration
并触发一个我们可以监听的事件,并将其作为事件属性swUpdated
发送。ServiceWorkerRegistration
制作更新混合
接下来,我们需要在 Vue 应用中监听这个事件。这段代码可以放在很多地方,具体取决于你的项目结构,但我选择将其放在 mixin 中。这只是个人喜好,你自己决定吧。我们在src
called中创建一个文件mixins/update.js
,并设置它来监听事件,并在触发时进行回调:
export default {
created() {
document.addEventListener('swUpdated', this.updateAvailable, { once: true })
},
methods: {
updateAvailable(event) {
console.log(event)
}
}
}
关于该选项的说明once
;将此选项设置为 true 只允许调用一次侦听器,并且一旦调用就会删除该侦听器。
让我们存储 SW 注册信息,以便稍后在更新过程中使用。同时,我们可以添加一个标志来控制将来是否向用户显示“更新可用;请刷新”消息。应该如下所示:
export default {
data() {
return {
registration: null,
updateExists: false,
}
},
created() {
document.addEventListener('swUpdated', this.updateAvailable, { once: true })
},
methods: {
updateAvailable(event) {
this.registration = event.detail
this.updateExists = true
}
}
}
更新我们的 UI
我使用 mixin 的原因之一是,这样我就可以轻松地在应用程序的任何地方(App.vue、布局或其他位置)以及我在该项目中使用的任何 UI 工具包中使用此功能。我非常喜欢Vuetify,所以在本教程中,我们用它来向用户推送“更新”消息。
为了简单起见,我们直接把我们的App.vue
文件放进去就行了。同样,你可以在适合你应用的任何位置执行此操作。
在你的App.vue
模板中添加一个带有按钮的 Snackbar 组件,当系统提示时,用户可以点击该按钮来更新应用。像这样:
<v-snackbar bottom right :value="updateExists" :timeout="0" color="primary">
An update is available
<v-btn text @click="refreshApp">
Update
</v-btn>
</v-snackbar>
您还需要导入更新 mixin。由于我们添加了 mixin,因此可以访问它的所有数据和函数。
跳过服务工作等待
让我们回到更新混合宏并创建refreshApp
函数。我们将使用此函数重置updateExists
标志,并强制新的 Service Worker 成为活动状态。Service Worker 注册后,它会“等待”,直到先前注册的 Service Worker 不再控制客户端。通过告诉新的 Service Worker“跳过等待”,我们实际上跳过了这个等待期。
我们的refreshApp
函数看起来有点像这样:
refreshApp() {
this.updateExists = false
// Make sure we only send a 'skip waiting' message if the SW is waiting
if (!this.registration || !this.registration.waiting) return
// Send message to SW to skip the waiting and activate the new SW
this.registration.waiting.postMessage({ type: 'SKIP_WAITING' })
}
更新我们的服务工作者
如果您使用的是默认设置plugin-pwa
或已设置workboxPluginMode
为'GenerateSW'
,则可以跳过此部分,因为插件会自动生成带有适当监听器的 Service Worker。否则,您需要在标准 Workbox 配置后将以下监听器添加到 Service Worker:
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting()
}
})
快完成了。现在,我们只需要在新的 Service Worker 激活后重新加载页面,这样更改就可以看到。
重新加载页面
回到我们的更新混合中,让我们监听controllerchange
来自服务人员的事件。
另外created()
:
navigator.serviceWorker.addEventListener('controllerchange', () => {
// We'll also need to add 'refreshing' to our data originally set to false.
if (this.refreshing) return
this.refreshing = true
// Here the actual reload of the page occurs
window.location.reload()
})
就这样!部署此更新并手动清除应用存储空间。然后部署另一个更新,刷新页面,您应该会看到弹窗:
单击更新按钮将触发网站重新加载,您将看到您的更改!
TL;DR
- 更新 serviceworker 注册:
// src/registerServiceWorker.js
// Standard SW registration script.
// Auto generated by the Vue CLI PWA Plugin
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
//...
// When the SW is updated we will dispatch an event we can listen to in our .vue file
updated(registration) {
console.log('New content is available; please refresh.')
document.dispatchEvent(
new CustomEvent('swUpdated', { detail: registration })
)
},
//...
})
}
- 制作一个更新混合:
// src/mixins/update.js
export default {
data() {
return {
// refresh variables
refreshing: false,
registration: null,
updateExists: false,
}
},
created() {
// Listen for our custom event from the SW registration
document.addEventListener('swUpdated', this.updateAvailable, { once: true })
// Prevent multiple refreshes
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (this.refreshing) return
this.refreshing = true
// Here the actual reload of the page occurs
window.location.reload()
})
},
methods: {
// Store the SW registration so we can send it a message
// We use `updateExists` to control whatever alert, toast, dialog, etc we want to use
// To alert the user there is an update they need to refresh for
updateAvailable(event) {
this.registration = event.detail
this.updateExists = true
},
// Called when the user accepts the update
refreshApp() {
this.updateExists = false
// Make sure we only send a 'skip waiting' message if the SW is waiting
if (!this.registration || !this.registration.waiting) return
// send message to SW to skip the waiting and activate the new SW
this.registration.waiting.postMessage({ type: 'SKIP_WAITING' })
},
},
}
- 更新 UI(vuetify 示例):
// src/App.vue
// I use Vuetify in almost all of my Vue apps so this is how __I__ handle alerting the user to an update.
// Your implementation may change based on your UI
<template>
<!-- normal vue views stuff here -->
<!-- ... -->
<v-snackbar bottom right :value="updateExists" :timeout="0" color="primary">
An update is available
<v-btn text @click="refreshApp">
Update
</v-btn>
</v-snackbar>
</template>
<script>
import update from './mixins/update'
export default {
name: 'App',
data: () => ({
//
}),
mixins: [update],
...
}
</script>
- 更新服务工作者:
// src/service-worker.js
// If you are using 'GenerateSW' (default) for your workboxPluginMode setting this file is auto generated for you.
// If you are using 'InjectManifest' then add this to your custom SW after your standard workbox config
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting()
}
})
轰隆,完成了。
那么,你觉得怎么样?我的实现有什么需要改进的地方吗?你会用不同的方式处理软件更新吗?就算你只是不喜欢我的写作风格,也请告诉我。如果没有你们的反馈,我就不会进步,也不会有信心写更多文章!
文章来源:https://dev.to/drbragg/handling-service-worker-updates-in-your-vue-pwa-1pip