Angular Service Worker通信

2022-07-12 09:56 更新

與 Service Worker 通訊

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

SwUpdate 服務(wù)

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

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

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

版本更新

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

事件類型

詳情

VersionDetectedEvent

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

NoNewVersionDetectedEvent

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

VersionReadyEvent

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

VersionInstallationFailedEvent

在新版本安裝失敗時(shí)發(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 會(huì)在初始化和每次導(dǎo)航請(qǐng)求(也就是用戶導(dǎo)航到應(yīng)用中的另一個(gè)地址)時(shí)檢查更新。不過(guò),如果你的站點(diǎn)更新非常頻繁,或者需要按計(jì)劃進(jìn)行更新,你可能會(huì)選擇手動(dòng)檢查更新。

通過(guò)調(diào)用 ?checkForUpdate()? 方法來(lái)實(shí)現(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());
  }
}

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

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

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

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

強(qiáng)制激活更新

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

@Injectable()
export class PromptUpdateService {

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

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

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

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

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

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

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

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

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

@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)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)