在您的 Vue PWA 中处理 Service Worker 更新 更新 Service Worker 注册 进行更新混合 更新我们的 UI 跳过服务工作等待 更新我们的服务 Worker 重新加载页面 TL;DR

2025-05-27

在 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.')
}
Enter fullscreen mode Exit fullscreen mode

我们需要稍微修改一下这个函数,让它在有更新时不仅仅记录到我们的控制台上。

首先,我们需要访问刚刚注册的 Service Worker。幸运的是,Service Workerregister-service-worker帮我们搞定了这个问题。根据他们的文档:

、、事件在ready参数中传递一个ServiceWorkerRegistrationregistered实例。cachedupdatefoundupdated

完美!只需将ServiceWorkerRegistrationin 作为参数传递,我们就可以开始比赛了。接下来我们要面对的问题是将这些注册详细信息发送到我们的 Vue 应用。因此,我们可以使用CustomEvent来处理这个问题。现在我们的update()函数应该如下所示:

updated(registration) {
  console.log('New content is available; please refresh.')
  document.dispatchEvent(
    new CustomEvent('swUpdated', { detail: registration })
  )
}
Enter fullscreen mode Exit fullscreen mode

我们现在传入ServiceWorkerRegistration并触发一个我们可以监听的事件,并将其作为事件属性swUpdated发送。ServiceWorkerRegistration

制作更新混合

接下来,我们需要在 Vue 应用中监听这个事件。这段代码可以放在很多地方,具体取决于你的项目结构,但我选择将其放在 mixin 中。这只是个人喜好,你自己决定吧。我们在srccalled中创建一个文件mixins/update.js,并设置它来监听事件,并在触发时进行回调:

export default {
  created() {
    document.addEventListener('swUpdated', this.updateAvailable, { once: true })
  },
  methods: {
    updateAvailable(event) {
      console.log(event)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

关于该选项的说明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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

更新我们的 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>
Enter fullscreen mode Exit fullscreen mode

您还需要导入更新 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' })
}
Enter fullscreen mode Exit fullscreen mode

更新我们的服务工作者

如果您使用的是默认设置plugin-pwa或已设置workboxPluginMode'GenerateSW',则可以跳过此部分,因为插件会自动生成带有适当监听器的 Service Worker。否则,您需要在标准 Workbox 配置后将以下监听器添加到 Service Worker:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting()
  }
})
Enter fullscreen mode Exit fullscreen mode

快完成了。现在,我们只需要在新的 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()
})
Enter fullscreen mode Exit fullscreen mode

就这样!部署此更新并手动清除应用存储空间。然后部署另一个更新,刷新页面,您应该会看到弹窗:

单击更新按钮将触发网站重新加载,您将看到您的更改!

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 })
      )
    },
    //...
  })
}
Enter fullscreen mode Exit fullscreen mode
  • 制作一个更新混合:
// 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' })
    },
  },
}
Enter fullscreen mode Exit fullscreen mode
  • 更新 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>
Enter fullscreen mode Exit fullscreen mode
  • 更新服务工作者:
// 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()
  }
})
Enter fullscreen mode Exit fullscreen mode

轰隆,完成了。

那么,你觉得怎么样?我的实现有什么需要改进的地方吗?你会用不同的方式处理软件更新吗?就算你只是不喜欢我的写作风格,也请告诉我。如果没有你们的反馈,我就不会进步,也不会有信心写更多文章!

文章来源:https://dev.to/drbragg/handling-service-worker-updates-in-your-vue-pwa-1pip
PREV
gRPC 指南
NEXT
使用 Gatsby 和 Sanity 开展新项目 为什么要使用 Gatsby 和 Sanity?我们正在做什么 步骤 就是这样!