ارتباط با 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 و شفافیت، جزئیات خطا را ارائه میکند. |
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() انجام دهید:
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 صفحه و بهروزرسانی به آخرین نسخه مشکلی ندارد:
// #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 باشند.
- کاربر برای نخستین بار برنامه را باز میکند و service worker آخرین نسخه برنامه را cache میکند.
این نسخه جدیدتر فایلهای index.html، main.<main-hash-2>.js و lazy-chunk.<lazy-hash-2>.js را در بر دارد.
- کاربر برنامه را میبندد و مدتی آن را باز نمیکند.
- پس از مدتی، نسخه جدیدی از برنامه روی server مستقر میشود.
مرورگرها ممکن است برای آزاد کردن فضای دیسک، منابع مشخص یا همه منابع را از cache حذف کنند.
- در این فاصله، مرورگر کاربر تصمیم میگیرد
lazy-chunk.<lazy-hash-1>.jsرا از cache خود حذف کند.
service worker جدیدترین نسخهای را که در این لحظه میشناسد، یعنی نسخه قدیمی \(index.html و main.<main-hash-1>.js\)، ارائه میدهد.
- کاربر دوباره برنامه را باز میکند.
همچنین نمیتواند آن را از server دریافت کند \(زیرا server اکنون فقط lazy-chunk.<lazy-hash-2>.js متعلق به نسخه جدیدتر را دارد\).
- مدتی بعد، برنامه bundle از نوع lazy یعنی
lazy-chunk.<lazy-hash-1>.jsرا درخواست میکند. - service worker نمیتواند asset را در cache پیدا کند \(به یاد داشته باشید مرورگر آن را حذف کرده است\).
در سناریوی بالا، service worker نمیتواند assetی را ارائه کند که معمولاً باید cache شده باشد. آن نسخه مشخص برنامه خراب است و بدون reload صفحه راهی برای اصلاح وضعیت client وجود ندارد. در چنین مواردی، service worker با ارسال رویداد UnrecoverableStateEvent به client اطلاع میدهد. برای دریافت اعلان و مدیریت این خطاها در SwUpdate#unrecoverable مشترک شوید.
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
ممکن است مطالب زیر نیز برایتان مفید باشند: