آنلاین

ارتباط با Service Worker

فعال کردن پشتیبانی service worker فقط آن را ثبت نمی‌کند؛ بلکه serviceهایی نیز در اختیارتان قرار می‌دهد که برای تعامل با service worker و کنترل cache برنامه قابل استفاده‌اند.

service به نام SwUpdate

service به نام SwUpdate امکان دسترسی به رویدادهایی را فراهم می‌کند که زمان یافتن و نصب یک به‌روزرسانی موجود برای برنامه توسط service worker را نشان می‌دهند.

service به نام SwUpdate از سه عملیات جداگانه پشتیبانی می‌کند:

  • دریافت اعلان هنگامی که نسخه به‌روزشده روی server شناسایی شده، نصب و آماده استفاده محلی است یا نصب آن شکست خورده است.
  • درخواست از service worker برای بررسی به‌روزرسانی‌های جدید روی server.
  • درخواست از service worker برای فعال کردن آخرین نسخه برنامه در tab فعلی.

به‌روزرسانی نسخه

versionUpdates یک ویژگی Observable در SwUpdate است و پنج نوع رویداد emit می‌کند:

نوع رویدادجزئیات
VersionDetectedEventهنگامی emit می‌شود که service worker نسخه جدیدی از برنامه را روی server یافته و در آستانه دانلود آن است.
NoNewVersionDetectedEventهنگامی emit می‌شود که service worker نسخه برنامه را روی server بررسی کرده، اما نسخه جدیدی نیافته است.
VersionReadyEventهنگامی emit می‌شود که نسخه جدید برنامه برای فعال‌سازی توسط clientها آماده است. می‌توان از آن برای آگاه کردن کاربر از به‌روزرسانی موجود یا درخواست refresh صفحه استفاده کرد.
VersionInstallationFailedEventهنگامی emit می‌شود که نصب نسخه جدید شکست خورده است. می‌توان از آن برای logging/monitoring استفاده کرد.
VersionFailedEventهنگامی emit می‌شود که یک نسخه با خرابی بحرانی \(مانند خطای hash خراب\) روبه‌رو شده که همه clientهای استفاده‌کننده از آن نسخه را تحت تأثیر قرار می‌دهد. برای debugging و شفافیت، جزئیات خطا را ارائه می‌کند.
log-update.service.ts
import {inject, Injectable} from '@angular/core';
import {SwUpdate, VersionReadyEvent} from '@angular/service-worker';

// #docregion sw-update
@Injectable({providedIn: 'root'})
export class LogUpdateService {
  private updates = inject(SwUpdate);
  constructor() {
    this.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;
        case 'VERSION_FAILED':
          console.log(`Version '${evt.version.hash}' failed with error: ${evt.error}`);
          break;
      }
    });
  }
}
// #enddocregion sw-update

بررسی به‌روزرسانی‌ها

می‌توان از service worker خواست بررسی کند آیا به‌روزرسانی‌ای روی server مستقر شده است یا خیر. service worker هنگام مقداردهی اولیه و در هر درخواست پیمایش — یعنی وقتی کاربر از نشانی دیگری وارد برنامه می‌شود — به‌روزرسانی‌ها را بررسی می‌کند. با این حال، اگر سایتی دارید که مرتب تغییر می‌کند یا می‌خواهید به‌روزرسانی‌ها طبق زمان‌بندی انجام شوند، می‌توانید آن‌ها را به‌صورت دستی بررسی کنید.

این کار را با متد checkForUpdate() انجام دهید:

check-for-update.service.ts
import {ApplicationRef, inject, Injectable} from '@angular/core';
import {SwUpdate} from '@angular/service-worker';
import {concat, interval} from 'rxjs';
import {first} from 'rxjs/operators';

@Injectable({providedIn: 'root'})
export class CheckForUpdateService {
  private appRef = inject(ApplicationRef);
  private updates = inject(SwUpdate);

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

    everySixHoursOnceAppIsStable$.subscribe(async () => {
      try {
        const updateFound = await this.updates.checkForUpdate();
        console.log(updateFound ? 'A new version is available.' : 'Already on the latest version.');
      } catch (err) {
        console.error('Failed to check for updates:', err);
      }
    });
  }
}

این متد یک Promise<boolean> برمی‌گرداند که آماده بودن به‌روزرسانی برای فعال‌سازی را نشان می‌دهد. ممکن است بررسی شکست بخورد و باعث reject شدن Promise شود.

به‌روزرسانی به آخرین نسخه

می‌توانید به‌محض آماده شدن نسخه جدید، با reload کردن صفحه tab موجود را به آخرین نسخه به‌روزرسانی کنید. برای مختل نشدن کار کاربر، معمولاً بهتر است از او درخواست کنید تأیید کند که reload صفحه و به‌روزرسانی به آخرین نسخه مشکلی ندارد:

prompt-update.service.ts
// #docplaster
import {inject, Injectable} from '@angular/core';
// #docregion sw-replicate-available
import {filter, map} from 'rxjs/operators';
// #enddocregion sw-replicate-available
import {SwUpdate, VersionReadyEvent} from '@angular/service-worker';

function promptUser(event: VersionReadyEvent): boolean {
  return true;
}

// #docregion sw-version-ready
@Injectable({providedIn: 'root'})
export class PromptUpdateService {
  constructor() {
    const swUpdate = inject(SwUpdate);
    swUpdate.versionUpdates
      .pipe(filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'))
      .subscribe((evt) => {
        if (promptUser(evt)) {
          // Reload the page to update to the latest version.
          document.location.reload();
        }
      });
    // #enddocregion sw-version-ready
    // #docregion sw-replicate-available
    // ...
    const updatesAvailable = swUpdate.versionUpdates.pipe(
      filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
      map((evt) => ({
        type: 'UPDATE_AVAILABLE',
        current: evt.currentVersion,
        available: evt.latestVersion,
      })),
    );
    // #enddocregion sw-replicate-available
    // #docregion sw-version-ready
  }
}
// #enddocregion sw-version-ready

مدیریت وضعیت غیرقابل‌بازیابی

گاهی ممکن است نسخه‌ای از برنامه که service worker برای ارائه به client استفاده می‌کند، در وضعیتی خراب قرار بگیرد که بدون reload کامل صفحه قابل‌بازیابی نباشد.

برای نمونه، سناریوی زیر را در نظر بگیرید:

فرض کنید assetهای cacheشده برنامه شامل index.html،‏ main.<main-hash-1>.js و lazy-chunk.<lazy-hash-1>.js باشند.

  1. کاربر برای نخستین بار برنامه را باز می‌کند و service worker آخرین نسخه برنامه را cache می‌کند.

این نسخه جدیدتر فایل‌های index.html،‏ main.<main-hash-2>.js و lazy-chunk.<lazy-hash-2>.js را در بر دارد.

  1. کاربر برنامه را می‌بندد و مدتی آن را باز نمی‌کند.
  2. پس از مدتی، نسخه جدیدی از برنامه روی server مستقر می‌شود.

مرورگرها ممکن است برای آزاد کردن فضای دیسک، منابع مشخص یا همه منابع را از cache حذف کنند.

  1. در این فاصله، مرورگر کاربر تصمیم می‌گیرد lazy-chunk.<lazy-hash-1>.js را از cache خود حذف کند.

service worker جدیدترین نسخه‌ای را که در این لحظه می‌شناسد، یعنی نسخه قدیمی \(index.html و main.<main-hash-1>.js\)، ارائه می‌دهد.

  1. کاربر دوباره برنامه را باز می‌کند.

همچنین نمی‌تواند آن را از server دریافت کند \(زیرا server اکنون فقط lazy-chunk.<lazy-hash-2>.js متعلق به نسخه جدیدتر را دارد\).

  1. مدتی بعد، برنامه bundle از نوع lazy یعنی lazy-chunk.<lazy-hash-1>.js را درخواست می‌کند.
  2. service worker نمی‌تواند asset را در cache پیدا کند \(به یاد داشته باشید مرورگر آن را حذف کرده است\).

در سناریوی بالا، service worker نمی‌تواند assetی را ارائه کند که معمولاً باید cache شده باشد. آن نسخه مشخص برنامه خراب است و بدون reload صفحه راهی برای اصلاح وضعیت client وجود ندارد. در چنین مواردی، service worker با ارسال رویداد UnrecoverableStateEvent به client اطلاع می‌دهد. برای دریافت اعلان و مدیریت این خطاها در SwUpdate#unrecoverable مشترک شوید.

handle-unrecoverable-state.service.ts
import {inject, Injectable} from '@angular/core';
import {SwUpdate} from '@angular/service-worker';

function notifyUser(message: string): void {}

// #docregion sw-unrecoverable-state
@Injectable({providedIn: 'root'})
export class HandleUnrecoverableStateService {
  private updates = inject(SwUpdate);
  constructor() {
    this.updates.unrecoverable.subscribe((event) => {
      notifyUser(
        'An error occurred that we cannot recover from:\n' +
          event.reason +
          '\n\nPlease reload the page.',
      );
    });
  }
}
// #enddocregion sw-unrecoverable-state

مطالب بیشتر درباره service workerهای Angular

ممکن است مطالب زیر نیز برایتان مفید باشند: