Vue PWA에서 서비스 워커 업데이트 처리

27791 단어 vuejavascriptwebdevpwa

목차


  • Updating the Service Worker registration
  • Making an update mixin
  • Updating our UI
  • Skipping Service Working waiting
  • Updating our Service Worker
  • Reloading the page
  • TL;DR

  • Vue.js는 굉장합니다. 사용하기 쉽고 매우 유연하며 몇 가지 멋진 DevTools가 있습니다. 당신이 이것을 읽고 있기 때문에 나는 당신이 이미 이것을 알고 있다고 가정합니다.

    Vue CLI는 이러한 DevTool 중 하나로 Vue 앱에 플러그인을 빠르고 쉽게 추가할 수 있습니다. PWA 빌드의 증가하는 추세와 인기를 감안할 때 Vue CLI에 자체 PWA 플러그인이 있으며 대부분의 경우 예상한 만큼 훌륭합니다.

    당신이 하려는 모든 것이 당신의 사이트에 기본적인 PWA 마법을 추가하는 것이라면 plugin-pwa는 순수한 마법입니다. 설치하기만 하면 설치 가능성에 대한 매니페스트와 사전 캐싱을 위한 서비스 워커를 즉시 얻을 수 있습니다. 테마 색상으로 멋지게 꾸미고 PWA 이름을 변경하는 등 다양한 옵션configuration이 있습니다.

    기본적으로 수행되지 않는 것은 업데이트된 서비스 워커가 발견되었을 때 서비스 워커를 활성화하는 처리입니다. 그래서 직접 추가해 보겠습니다.

    서비스 워커 등록 업데이트

    When you install the plugin-pwa it adds a registerServiceWorker.js file to src with some basic config and events. For more on this file feel free to checkout register-service-worker npm. (이 튜토리얼에서) 필요한 유일한 부분은 update() 함수입니다. 새로 설치하면 다음과 같이 표시됩니다.

    updated () {
      console.log('New content is available; please refresh.')
    }
    


    업데이트가 있을 때 콘솔에 로그인하는 것 이상을 수행하도록 이 함수를 약간 수정해야 합니다.

    먼저, 방금 등록한 새 서비스 워커에 대한 액세스 권한이 필요합니다. 운 좋게도register-service-worker가 이를 처리합니다. 그들의 문서에 따르면:

    The ready, registered, cached, updatefound and updated events passes a ServiceWorkerRegistration instance in their arguments.



    완벽한! ServiceWorkerRegistration를 인수로 전달하기만 하면 경주가 시작됩니다. 우리가 직면하게 될 다음 문제는 등록 세부 정보를 Vue 앱으로 가져오는 것입니다. 따라서 CustomEvent을 사용하여 처리할 수 있습니다. 이제 update() 함수는 다음과 같아야 합니다.

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


    이제 우리는 ServiceWorkerRegistration를 전달하고 호출된 swUpdated를 수신할 수 있는 이벤트를 트리거하고 ServiceWorkerRegistration를 이벤트 속성으로 보냅니다.

    업데이트 믹스인 만들기

    Next up is listening for this event from within our Vue app. There are many places you can put this code, depending on your projects structure, but I opted to make it a mixin. Just personal preference, you do you. Let's create a file in src called mixins/update.js and set it up to listen for our event and make a callback when it's triggered:

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

    A note about the once option; setting this option to true allows the listener to be called only once AND removes the listener once invoked.

    Let's store the SW registration so we can use it later in the update process. While we're at it we can add a flag to control showing our future 'Update available; please refresh.' message to our user. Should look something like this:

    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 업데이트

    One of the reasons why I used a mixin for this is so I can easily use this functionality anywhere I want in my app (App.vue, a layout, somewhere else) and with any UI kit I'm using on that project. I love Vuetify 따라서 이 튜토리얼을 위해 사용자에게 '업데이트' 메시지를 전달할 수 있습니다.

    그리고 단순함을 위해 우리의 App.vue 파일을 넣습니다. 다시 말하지만, 앱에 적합한 모든 곳에서 이 작업을 수행할 수 있습니다.
    App.vue 템플릿에서 사용자가 메시지가 표시될 때 앱을 업데이트할 수 있도록 하는 버튼이 있는 스낵바 구성 요소를 추가합니다. 이 같은:

    <v-snackbar bottom right :value="updateExists" :timeout="0" color="primary">
      An update is available
      <v-btn text @click="refreshApp">
        Update
      </v-btn>
    </v-snackbar>
    


    또한 업데이트 믹스인을 가져와야 합니다. 믹스인을 추가하기 때문에 믹스인의 모든 데이터와 기능에 액세스할 수 있습니다.

    건너뛰는 서비스 작업 대기 중

    Let's pop back into our update mixin and create the refreshApp function. We'll use this function to reset the updateExists flag and force the new service worker to become the active one. Once a service worker is registered it "waits" until the perviously registered SW is no longer controlling the client. By telling the new SW to "skip waiting" we literally skip this waiting period.

    Our refreshApp function will look a little something like this:

    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' })
    }
    

    서비스 워커 업데이트

    If you're using the default settings for plugin-pwa or you have workboxPluginMode set to 'GenerateSW' you can skip this next part as the plugin automatically generates a service worker with the proper listener. Otherwise you need to add the following listener to your service worker after your standard workbox config:

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

    We're almost done. Now we just need to reload the page once the new service worker is active so our changes can be seen.

    페이지 새로고침

    Back in our update mixin lets listen for the controllerchange event from our service worker.

    In created() add:

    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()
    })
    

    And that's it! Deploy this update and manually clear your apps storage. Then deploy another update, refresh the page, and you should see your popup:

    Clicking the update button should trigger the site to reload and you'll see your changes!

    TL;DR

    • Update serviceworker registration:
    // 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 })
          )
        },
        //...
      })
    }
    
    • Make an update mixin:
    // 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' })
        },
      },
    }
    
    • Update the UI (vuetify example):
    // 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>
    
    • Update the Service Worker:
    // 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()
      }
    })
    

    붐, 완료.



    그래서 당신은 어떻게 생각하십니까? 내 구현에 대해 변경할 사항이 있습니까? SW 업데이트를 다르게 처리합니까? 젠장, 내 글 스타일이 마음에 들지 않으면 말해주세요. 당신의 피드백 없이는 더 나아지거나 더 많은 게시물을 작성할 자신이 없습니다!

    좋은 웹페이지 즐겨찾기