Angular Service Worker通信

2022-07-12 09:56 更新

與 Service Worker 通訊

把 ?ServiceWorkerModule ?導(dǎo)入到你的 ?AppModule? 中不僅會注冊 Service Worker,還會提供一些服務(wù),讓你能和 Service Worker 通訊,并控制你的應(yīng)用緩存。

SwUpdate 服務(wù)

?SwUpdate ?服務(wù)讓你能訪問一些事件,這些事件會指出 Service Worker 何時發(fā)現(xiàn)并安裝了可用的更新

?SwUpdate ?服務(wù)支持四個獨立的操作:

  • 當在服務(wù)器上檢測到新版本、已安裝并可本地使用或安裝失敗時獲得通知
  • 要求 Service Worker 檢查服務(wù)器上是否有更新。
  • 要求 Service Worker 為當前標簽頁激活應(yīng)用的最新版本

版本更新

?versionUpdates ?是 ?SwUpdate ?的一個 ?Observable ?屬性,并且會發(fā)出四種事件類型:

事件類型

詳情

VersionDetectedEvent

當 Service Worker 在服務(wù)器上檢測到應(yīng)用程序的新版本并即將開始下載時發(fā)出。

NoNewVersionDetectedEvent

當 Service Worker 檢查了服務(wù)器上應(yīng)用程序的版本并且沒有找到新版本時發(fā)出。

VersionReadyEvent

當有新版本的應(yīng)用程序可供客戶端激活時發(fā)出。它可用于通知用戶可用的更新或提示他們刷新頁面。

VersionInstallationFailedEvent

在新版本安裝失敗時發(fā)出。它可用于日志/監(jiān)控目的。

@Injectable()
export class LogUpdateService {

  constructor(updates: SwUpdate) {
    updates.versionUpdates.subscribe(evt => {
      switch (evt.type) {
        case 'VERSION_DETECTED':
          console.log(`Downloading new app version: ${evt.version.hash}`);
          break;
        case 'VERSION_READY':
          console.log(`Current app version: ${evt.currentVersion.hash}`);
          console.log(`New app version ready for use: ${evt.latestVersion.hash}`);
          break;
        case 'VERSION_INSTALLATION_FAILED':
          console.log(`Failed to install app version '${evt.version.hash}': ${evt.error}`);
          break;
      }
    });
  }
}

檢查更新

可以要求 Service Worker 檢查是否有任何更新已經(jīng)發(fā)布到了服務(wù)器上。Service Worker 會在初始化和每次導(dǎo)航請求(也就是用戶導(dǎo)航到應(yīng)用中的另一個地址)時檢查更新。不過,如果你的站點更新非常頻繁,或者需要按計劃進行更新,你可能會選擇手動檢查更新。

通過調(diào)用 ?checkForUpdate()? 方法來實現(xiàn):

import { ApplicationRef, Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { concat, interval } from 'rxjs';
import { first } from 'rxjs/operators';

@Injectable()
export class CheckForUpdateService {

  constructor(appRef: ApplicationRef, updates: SwUpdate) {
    // Allow the app to stabilize first, before starting
    // polling for updates with `interval()`.
    const appIsStable$ = appRef.isStable.pipe(first(isStable => isStable === true));
    const everySixHours$ = interval(6 * 60 * 60 * 1000);
    const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);

    everySixHoursOnceAppIsStable$.subscribe(() => updates.checkForUpdate());
  }
}

該方法返回一個用來表示檢查更新已經(jīng)成功完成的 ?Promise<boolean>?。這種檢查可能會失敗,它會導(dǎo)致此 ?Promise ?被拒絕(reject)。

為了避免影響頁面的首次渲染,在注冊 ServiceWorker 腳本之前,?ServiceWorkerModule ?默認會在應(yīng)用程序達到穩(wěn)定態(tài)之前等待最多 30 秒。如果不斷輪詢更新(比如調(diào)用 setInterval() 或 RxJS 的  interval())就會阻止應(yīng)用程序達到穩(wěn)定態(tài),則直到 30 秒結(jié)束之前都不會往瀏覽器中注冊 ServiceWorker 腳本。

注意:
應(yīng)用中所執(zhí)行的各種輪詢都會阻止它達到穩(wěn)定態(tài)。

可以通過在開始輪詢更新之前先等應(yīng)用達到穩(wěn)定態(tài)來消除這種延遲,如上述例子所示。另外,你還可以為 ServiceWorker 定義不一樣的 注冊策略。

強制激活更新

如果當前標簽頁需要立即更新到最新的應(yīng)用版本,可以通過 ?activateUpdate()? 方法來要求立即這么做:

@Injectable()
export class PromptUpdateService {

  constructor(updates: SwUpdate) {
    updates.available.subscribe(event => {
      if (promptUser(event)) {
        updates.activateUpdate().then(() => document.location.reload());
      }
    });
  }
}

如果調(diào)用 ?activateUpdate()? 而不刷新頁面,可能會破壞正在運行的應(yīng)用中的惰性加載模塊,特別是如果惰性加載的模塊文件名中使用了哈希時,就會改變每一個版本。所以,建議每當 ?activateUpdate()? 返回的 Promise 被解析時,都刷新一次頁面。

處理不可恢復(fù)的狀態(tài)

在某些情況下,Service Worker 用來為客戶端提供服務(wù)的應(yīng)用版本可能處于損壞狀態(tài),如果不重新加載整個頁面,則無法恢復(fù)該狀態(tài)。

比如,設(shè)想以下情形:

  • 用戶首次打開該應(yīng)用,Service Worker 會緩存該應(yīng)用的最新版本。假設(shè)應(yīng)用要緩存的資源包括 ?index.html?、?main.<main-hash-1>.js? 和 ?lazy-chunk.<lazy-hash-1>.js?。
  • 用戶關(guān)閉該應(yīng)用程序,并且有一段時間沒有打開它。
  • 一段時間后,會將新版本的應(yīng)用程序部署到服務(wù)器。新版本中包含文件 ?index.html?、?main.<main-hash-2>.js? 和 ?lazy-chunk.<lazy-hash-2>.js?。
  • 注意:
    哈希值現(xiàn)在已經(jīng)不同了,因為文件的內(nèi)容已經(jīng)改變)。服務(wù)器上不再提供舊版本。

    舊版本在服務(wù)器上不再可用。

  • 同時,用戶的瀏覽器決定從其緩存中清退 ?lazy-chunk.<lazy-hash-1>.js? 瀏覽器可能決定從緩存中清退特定(或所有)資源,以便回收磁盤空間。
  • 用戶再次打開本應(yīng)用。此時,Service Worker 將提供它所知的最新版本,當然,實際上對我們是舊版本(?index.html? 和 ?main.<main-hash-1>.js?)。
  • 在稍后的某個時刻,該應(yīng)用程序請求惰性捆綁包 ?lazy-chunk.<lazy-hash-1>.js?。
  • Service Worker 無法在緩存中找到該資產(chǎn)(請記住瀏覽器已經(jīng)將其清退了)。它也無法從服務(wù)器上獲取它(因為服務(wù)器現(xiàn)在只有較新版本的 ?lazy-chunk.<lazy-hash-2>.js?)。

在上述情況下,Service Worker 將無法提供通常會被緩存的資產(chǎn)。該特定的應(yīng)用程序版本已損壞,并且無法在不重新加載頁面的情況下修復(fù)客戶端的狀態(tài)。在這種情況下,Service Worker 會通過發(fā)送 ?UnrecoverableStateEvent ?事件來通知客戶端??梢杂嗛?nbsp;?SwUpdate#unrecoverable? 以得到通知并處理這些錯誤。

@Injectable()
export class HandleUnrecoverableStateService {
  constructor(updates: SwUpdate) {
    updates.unrecoverable.subscribe(event => {
      notifyUser(
        'An error occurred that we cannot recover from:\n' +
        event.reason +
        '\n\nPlease reload the page.'
      );
    });
  }
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號