Angular + PWA: Vytvořte instalovatelnou aplikaci
Le Progresivní webové aplikace (PWA) představují most mezi webovými aplikacemi a nativní aplikace. Díky technologiím jako I Servisní pracovníci, The Manifest webové aplikace e le Oznámení push, nabízí PWA zkušenosti srovnatelné s aplikacemi nainstalovanými z obchodu, ale bez nákladů a složitost nativní distribuce. Angular nativně integruje podporu PWA, transformace webové aplikace na instalovatelnou aplikaci je proces jednoduché a dobře zdokumentované.
V tomto osmém článku série Moderní hranatý prozkoumáme každý aspekt vytvoření PWA s Angular: od počáteční konfigurace po pokročilou správu mezipaměti, od offline podpory po push notifikace a aktualizační mechanismy automatická a vlastní výzva k instalaci. Podíváme se na to, jak testovat a ladit každý z nich funkčnost s nástroji Chrome DevTools.
Co se dozvíte v tomto článku
- Co jsou PWA a jaké výhody nabízejí oproti nativním aplikacím
- Jak nastavit Angular PWA pomocí
ng add @angular/pwa - Přizpůsobení Manifestu webové aplikace pro ikony, barvy a režimy zobrazení
- Konfigurace Service Worker s
ngsw-config.json - Strategie ukládání do mezipaměti: prefetch, lazy, network-first, cache-first
- Offline podpora se záložními stránkami a IndexedDB
- Push oznámení se službou
SwPush - Spravujte aktualizace pomocí
SwUpdate - Nainstalujte vlastní výzvu s
beforeinstallprompt - Testování a ladění pomocí Chrome DevTools a Lighthouse
Přehled moderních hranatých řad
| # | Položka | Soustředit |
|---|---|---|
| 1 | Úhlové signály | Jemnozrnná odezva |
| 2 | Bezzónová detekce změn | Smazat Zone.js |
| 3 | Nové šablony @if, @for, @defer | Moderní řídicí tok |
| 4 | Samostatné komponenty | Architektura bez NgModule |
| 5 | Formy signálu | Responzivní formuláře se signály |
| 6 | SSR a přírůstková hydratace | Vykreslování na straně serveru |
| 7 | Core Web Vitals v Angular | Výkon a metriky |
| 8 | Jste zde - Angular PWA | Progresivní webové aplikace |
| 9 | Advanced Dependency Injection | DI strom třesoucí |
| 10 | Migrujte z úhlu 17 na 21 | Průvodce migrací |
1. Co jsou progresivní webové aplikace
Una Progresivní webové aplikace je webová aplikace, která využívá technologie moderní, aby nabídl uživatelskou zkušenost srovnatelnou s nativní aplikací. Termín „progresivní“ znamená, že aplikace funguje pro každého uživatele bez ohledu na prohlížeč používá, postupně zlepšuje zážitek z prohlížečů, které podporují pokročilé funkce.
Klíčové vlastnosti PWA
PWA se od tradičních webových aplikací liší řadou schopností, které přibližují je nativním aplikacím distribuovaným prostřednictvím obchodů.
PWA vs nativní aplikace vs tradiční webová aplikace
| Charakteristický | Webová aplikace | PWA | Nativní aplikace |
|---|---|---|---|
| Instalovatelné | No | Si | Si |
| Funguje offline | No | Si | Si |
| Push oznámení | No | Si | Si |
| Automatická aktualizace | Si | Si | Přes obchod |
| Hardwarový přístup | Omezený | Dobrý | Kompletní |
| Rozdělení | URL | URL + instalace | App Store |
| Náklady na vývoj | Bas | Bas | Vysoký |
| SEO | Si | Si | Ne (indexování obchodu) |
Kdy zvolit PWA
PWA jsou ideální volbou, když chcete oslovit co největší počet uživatelů jediná kódová základna. Jsou perfektní pro e-commerce, blogy, firemní dashboardy, nástroje produktivitu a jakoukoli aplikaci, která těží z offline přístupu a upozornění tlačit. Pokud aplikace vyžaduje hluboký přístup k hardwaru (pokročilé Bluetooth, NFC, senzory specifika), může být stále nutná nativní aplikace.
2. Nastavení Angular PWA
Angular poskytuje oficiální schéma, které automatizuje konfiguraci PWA. S a jediným příkazem, CLI vygeneruje všechny potřebné soubory: manifest, konfiguraci servisního pracovníka a výchozí ikony.
# Aggiungere PWA al progetto
ng add @angular/pwa
# Cosa viene generato:
# - src/manifest.webmanifest (Web App Manifest)
# - ngsw-config.json (configurazione Service Worker)
# - src/assets/icons/ (icone PWA in varie dimensioni)
# - Modifiche a angular.json (serviceWorker: true)
# - Modifiche a index.html (link al manifest, meta theme-color)
# - Modifiche a app.config.ts (ServiceWorkerModule / provideServiceWorker)
Konfigurace v app.config.ts
Po spuštění schématu, app.config.ts je aktualizován
automaticky registrovat servisního pracovníka. Registrace probíhá pouze v
výroby a po stabilizaci aplikace.
// app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideServiceWorker } from '@angular/service-worker';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
provideServiceWorker('ngsw-worker.js', {
enabled: !isDevMode(),
// Registra il SW dopo la stabilizzazione dell'app
registrationStrategy: 'registerWhenStable:30000'
})
]
};
Servisní pracovník a vývojové prostředí
Servisní pracovník je ve vývojovém režimu zakázán (isDevMode()) proč
agresivní ukládání do mezipaměti by narušovalo načítání za provozu a živé znovu načítání. Testovat
servisního pracovníka lokálně, musíte spustit produkční sestavení
ng build a obsluhovat statické soubory s HTTP serverem jako
http-server o npx serve.
3. Manifest webové aplikace
Il Manifest webové aplikace je soubor JSON, který popisuje vaši aplikaci prohlížeč a operační systém. Obsahuje informace, jako je název aplikace, ikony, barvy motivu a režim zobrazení. Bez platného manifestu, prohlížeč nenabízí možnost nainstalovat aplikaci.
{
"name": "La Mia App Angular PWA",
"short_name": "MiaApp",
"description": "Un'applicazione Angular PWA completa con supporto offline",
"theme_color": "#58a6ff",
"background_color": "#0d1117",
"display": "standalone",
"orientation": "portrait-primary",
"scope": "/",
"start_url": "/",
"id": "/",
"categories": ["productivity", "utilities"],
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
],
"screenshots": [
{
"src": "assets/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide",
"label": "Dashboard principale"
},
{
"src": "assets/screenshots/mobile.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow",
"label": "Vista mobile"
}
],
"shortcuts": [
{
"name": "Dashboard",
"short_name": "Dash",
"url": "/dashboard",
"icons": [{ "src": "assets/icons/shortcut-dashboard.png", "sizes": "96x96" }]
},
{
"name": "Impostazioni",
"short_name": "Settings",
"url": "/settings",
"icons": [{ "src": "assets/icons/shortcut-settings.png", "sizes": "96x96" }]
}
]
}
Režim manifestního zobrazení
| Režim | Popis | Use Case |
|---|---|---|
standalone |
Aplikace se zobrazí jako nativní aplikace bez adresního řádku | Nejběžnější volba pro PWA |
fullscreen |
Aplikace zabírá celou obrazovku bez uživatelského rozhraní prohlížeče | Hry, pohlcující prezentace |
minimal-ui |
Podobné jako samostatné, ale s minimálními ovládacími prvky navigace | Aplikace, které vyžadují navigaci zpět/vpřed |
browser |
Aplikace se otevře na standardní kartě prohlížeče | Tradiční webové stránky (nedoporučujeme pro PWA) |
4. Konfigurace servisního pracovníka
Soubor ngsw-config.json je srdcem konfigurace PWA v Angular.
Definuje, které zdroje jsou ukládány do mezipaměti, jak jsou aktualizovány a pomocí kterých
strategie. Angular automaticky vygeneruje soubor ngsw-worker.js během
produkční sestavení založené na této konfiguraci.
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app-shell",
"installMode": "prefetch",
"updateMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
]
}
}
],
"dataGroups": [
{
"name": "api-freshness",
"urls": ["/api/user/**", "/api/notifications/**"],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 50,
"maxAge": "1h",
"timeout": "5s"
}
},
{
"name": "api-performance",
"urls": ["/api/articles/**", "/api/products/**"],
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "1d"
}
}
],
"navigationUrls": [
"/**",
"!/__**"
]
}
assetGroups vs dataGroups
| Vlastnictví | assetGroups | datové skupiny |
|---|---|---|
| Rozsah | Statické zdroje aplikací (JS, CSS, obrázky) | Dynamická data z API |
| Verzování | Na základě hash souboru | Na základě času (maxAge) |
| installMode | prefetch o lazy |
N/A |
| strategie | N/A | freshness o performance |
| Aktualizovat | Když se změní verze aplikace | Na základě maxAge a časového limitu |
5. Strategie ukládání do mezipaměti
Volba strategie ukládání do mezipaměti je zásadní pro vyvážení výkonu a
čerstvost dat. Angular NGSW podporuje dvě hlavní strategie pro i
dataGroups: svěžest (nejprve síť) e
výkon (cache-first), stejně jako statické mezipaměti pro aktiva.
Detailní strategie ukládání do mezipaměti
| Strategie | Přednost | Chování | Use Case |
|---|---|---|---|
| přednačítání (skupiny aktiv) | Cache | Stáhněte si všechny prostředky ihned po instalaci SW | Shell aplikace, jádro JS a CSS |
| líný (skupiny aktiv) | Ukládání do mezipaměti na vyžádání | Ukládá zdroj pouze na vyžádání | Obrázky, písma, sekundární aktiva |
| svěžest (dataGroups) | Síť | Zkuste síť; pokud vypršel časový limit, použijte mezipaměť | Uživatelský profil, upozornění, data v reálném čase |
| výkon (dataGroups) | Cache | Použít mezipaměť, pokud je k dispozici a její platnost nevypršela; jinak síť | Blogové články, katalog produktů, statická data |
Strategie svěžest je ekvivalentní vzoru síť-první: servisní pracovník nejprve vyzkouší síť a pokud požadavek selže během časového limitu specifikována, je potřeba verze uložená v mezipaměti. Strategie výkon to je místo toho cache-first: potřebuje verzi uloženou v mezipaměti okamžitě (pokud nevypršela) a obnovte mezipaměť na pozadí.
Stale-While-Revalidate s Angular
Angular NGSW tuto strategii nativně nepodporuje stale-while-revalidate,
ale můžete to simulovat kombinací strategie výkon s a
maxAge stručný. Například nastavení "maxAge": "5m" cache
data okamžitě obslouží, ale po 5 minutách provede nový požadavek do sítě
při příštím přihlášení. Pokud potřebujete podrobnější kontrolu, zvažte její použití
přímo z Cache API s pracovníkem zákaznického servisu.
6. Podpora offline
Jednou z nejdůležitějších vlastností PWA je schopnost pracovat bez něj připojení k internetu. Angular automaticky zpracovává ukládání prostředků do mezipaměti staticky prostřednictvím servisního pracovníka, ale pro úplné offline používání je to nezbytné také spravovat stav připojení, záložní stránky a vytrvalost serveru místní data.
Detekce stavu připojení
// network-status.service.ts
import { Injectable, signal, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class NetworkStatusService {
private platformId = inject(PLATFORM_ID);
isOnline = signal(true);
connectionType = signal<string>('unknown');
constructor() {
if (isPlatformBrowser(this.platformId)) {
this.isOnline.set(navigator.onLine);
window.addEventListener('online', () => {
this.isOnline.set(true);
this.syncPendingData();
});
window.addEventListener('offline', () => {
this.isOnline.set(false);
});
// Network Information API (se disponibile)
const connection = (navigator as any).connection;
if (connection) {
this.connectionType.set(connection.effectiveType);
connection.addEventListener('change', () => {
this.connectionType.set(connection.effectiveType);
});
}
}
}
private async syncPendingData(): Promise<void> {
// Sincronizza i dati salvati localmente quando torna online
const pendingActions = await this.getPendingActions();
for (const action of pendingActions) {
try {
await fetch(action.url, action.options);
await this.removePendingAction(action.id);
} catch (err) {
console.error('Sync fallita per:', action.id);
}
}
}
private async getPendingActions(): Promise<any[]> {
// Recupera da IndexedDB le azioni in attesa
return [];
}
private async removePendingAction(id: string): Promise<void> {
// Rimuovi azione completata da IndexedDB
}
}
Stálost dat s IndexedDB
Chcete-li spravovat strukturovaná data offline, IndexedDB je to nejodolnější řešení.
Na rozdíl od localStorage (synchronní, limit 5-10MB), podporuje IndexedDB
velké objemy dat, indexované dotazy a asynchronní operace.
// offline-storage.service.ts
import { Injectable, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Injectable({ providedIn: 'root' })
export class OfflineStorageService {
private db: IDBDatabase | null = null;
private platformId = inject(PLATFORM_ID);
private readonly DB_NAME = 'pwa-offline-store';
private readonly DB_VERSION = 1;
async init(): Promise<void> {
if (!isPlatformBrowser(this.platformId)) return;
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
// Crea object stores per i dati offline
if (!db.objectStoreNames.contains('articles')) {
db.createObjectStore('articles', { keyPath: 'id' });
}
if (!db.objectStoreNames.contains('pendingActions')) {
const store = db.createObjectStore('pendingActions', {
keyPath: 'id', autoIncrement: true
});
store.createIndex('timestamp', 'timestamp');
}
};
request.onsuccess = (event) => {
this.db = (event.target as IDBOpenDBRequest).result;
resolve();
};
request.onerror = () => reject(request.error);
});
}
async save<T>(storeName: string, data: T): Promise<void> {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const tx = this.db!.transaction(storeName, 'readwrite');
tx.objectStore(storeName).put(data);
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
}
async getAll<T>(storeName: string): Promise<T[]> {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const tx = this.db!.transaction(storeName, 'readonly');
const request = tx.objectStore(storeName).getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
Omezení úložiště offline
- Sdílení prohlížeče: Prohlížeče omezují dostupné úložiště (obvykle 50–80 % místa na disku). USA
navigator.storage.estimate()pro kontrolu dostupného místa - Zásady vystěhování: V případě nedostatku místa může prohlížeč data smazat. Požádejte o trvalé úložiště s
navigator.storage.persist() - Bezpečnost SSR: IndexedDB na straně serveru neexistuje. Vždy používejte
isPlatformBrowsernež k němu přistoupíte - Konflikty synchronizace: Když se uživatel vrátí do režimu online, místní data mohou být v konfliktu s daty serveru. Implementujte strategii řešení konfliktů
7. Push Notification
Push notifikace umožňují vašemu PWA odesílat zprávy uživateli, i když
aplikace není aktivní. Angular poskytuje službu SwPush což zjednodušuje
správa vašich předplatných a přijímání oznámení prostřednictvím
Protokol Web Push.
Přihlášení k odběru oznámení Push
// push-notification.service.ts
import { Injectable, inject } from '@angular/core';
import { SwPush } from '@angular/service-worker';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class PushNotificationService {
private swPush = inject(SwPush);
private http = inject(HttpClient);
// Chiave pubblica VAPID (generata sul server)
private readonly VAPID_PUBLIC_KEY = 'BPxGEaVr...tua-chiave-pubblica...';
async subscribeToNotifications(): Promise<void> {
try {
// Richiedi il permesso e ottieni la sottoscrizione
const subscription = await this.swPush.requestSubscription({
serverPublicKey: this.VAPID_PUBLIC_KEY
});
console.log('Sottoscrizione push:', JSON.stringify(subscription));
// Invia la sottoscrizione al backend
await this.http.post('/api/push/subscribe', subscription).toPromise();
} catch (err) {
console.error('Errore sottoscrizione push:', err);
}
}
listenForNotifications(): void {
// Notifiche ricevute mentre l'app e in primo piano
this.swPush.messages.subscribe((message: any) => {
console.log('Notifica ricevuta:', message);
// Aggiorna la UI con i nuovi dati
});
// Clic sulla notifica (apre/focalizza l'app)
this.swPush.notificationClicks.subscribe((event) => {
console.log('Clic su notifica:', event.action, event.notification);
// Naviga alla pagina appropriata
const url = event.notification.data?.url;
if (url) {
window.open(url, '_blank');
}
});
}
async unsubscribe(): Promise<void> {
try {
const subscription = await this.swPush.subscription.toPromise();
if (subscription) {
await subscription.unsubscribe();
await this.http.post('/api/push/unsubscribe', {
endpoint: subscription.endpoint
}).toPromise();
}
} catch (err) {
console.error('Errore cancellazione sottoscrizione:', err);
}
}
}
Odesílání oznámení ze serveru
// server/push-server.ts (Node.js con web-push)
import webPush from 'web-push';
// Configurazione VAPID
webPush.setVapidDetails(
'mailto:info@example.com',
process.env.VAPID_PUBLIC_KEY!,
process.env.VAPID_PRIVATE_KEY!
);
async function sendNotification(
subscription: webPush.PushSubscription,
payload: object
): Promise<void> {
const notification = {
notification: {
title: 'Nuovo articolo disponibile!',
body: 'Scopri le novità su Angular PWA',
icon: '/assets/icons/icon-192x192.png',
badge: '/assets/icons/badge-72x72.png',
vibrate: [100, 50, 100],
data: {
url: '/blog/angular-pwa',
dateOfArrival: Date.now()
},
actions: [
{ action: 'leggi', title: 'Leggi Ora' },
{ action: 'chiudi', title: 'Chiudi' }
]
}
};
try {
await webPush.sendNotification(
subscription,
JSON.stringify(notification)
);
} catch (error: any) {
if (error.statusCode === 410) {
// Sottoscrizione scaduta: rimuovila dal database
await removeSubscription(subscription.endpoint);
}
}
}
Klíče VAPID
Klíče VAPID (Dobrovolná identifikace aplikačního serveru) se používají pro
ověřte svůj server pro službu push prohlížeče. Můžete je generovat pomocí
příkaz npx web-push generate-vapid-keys. Je nutné zadat veřejný klíč
na frontendu zůstává privátní výhradně na serveru. Nikdy nespáchat
soukromý klíč v úložišti.
8. Správa aktualizací
Když vydáte novou verzi vašeho PWA, servisní pracovník si musí stáhnout soubor
aktualizované zdroje a upozornit uživatele. Angular poskytuje službu
SwUpdate řídit životní cyklus aktualizace určitým způsobem
zaškrtnuto.
// app-update.service.ts
import {
Injectable, inject, ApplicationRef
} from '@angular/core';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { filter, first, interval, concat } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AppUpdateService {
private swUpdate = inject(SwUpdate);
private appRef = inject(ApplicationRef);
initUpdateCheck(): void {
if (!this.swUpdate.isEnabled) {
console.log('Service Worker non abilitato');
return;
}
// Controlla aggiornamenti ogni 6 ore
const appIsStable$ = this.appRef.isStable.pipe(
first(stable => stable)
);
const every6Hours$ = interval(6 * 60 * 60 * 1000);
const every6HoursOnceAppStable$ = concat(appIsStable$, every6Hours$);
every6HoursOnceAppStable$.subscribe(async () => {
try {
const updateFound = await this.swUpdate.checkForUpdate();
console.log(updateFound
? 'Nuovo aggiornamento trovato!'
: 'App già aggiornata');
} catch (err) {
console.error('Errore controllo aggiornamento:', err);
}
});
// Ascolta quando una nuova versione e pronta
this.swUpdate.versionUpdates.pipe(
filter((event): event is VersionReadyEvent =>
event.type === 'VERSION_READY'
)
).subscribe(event => {
console.log('Versione corrente:', event.currentVersion.hash);
console.log('Nuova versione:', event.latestVersion.hash);
this.promptUpdate();
});
// Gestisci errori irrecuperabili del SW
this.swUpdate.unrecoverable.subscribe(event => {
console.error('SW in stato irrecuperabile:', event.reason);
// Forza il reload completo
document.location.reload();
});
}
private promptUpdate(): void {
const shouldUpdate = confirm(
'Una nuova versione dell\'app è disponibile. ' +
'Vuoi aggiornare ora?'
);
if (shouldUpdate) {
// Attiva la nuova versione e ricarica
this.swUpdate.activateUpdate().then(() => {
document.location.reload();
});
}
}
}
Životní cyklus aktualizace PWA
| Fáze | Událost SwUpdate | Popis |
|---|---|---|
| 1. Detekce | VERSION_DETECTED |
SW našel na serveru novou verzi |
| 2. Stahování | VERSION_INSTALLATION_FAILED |
Chyba při stahování nové verze (pokud se nezdařilo) |
| 3. Připraveno | VERSION_READY |
Nová verze je stažena a připravena k aktivaci |
| 4. Aktivace | activateUpdate() |
Uživatel potvrdí a SW aktivuje novou verzi |
| 5. Dobijte | location.reload() |
Stránka se znovu načte s aktualizovanými zdroji |
Neopravitelná chyba servisního pracovníka
Událost unrecoverable znamená, že servisní pracovník již není schopen
správně zobrazovat aktuální verzi aplikace (například server odstranil
zdroje stále potřebné). V tomto případě je jediným řešením vynutit opětovné načtení
kompletní se stránkou document.location.reload(). Sledujte tuto událost
ve výrobě k identifikaci problémů s nasazením.
9. Vlastní výzva k instalaci
Prohlížeče automaticky zobrazí instalační banner, když PWA vyhovuje
kritéria instalovatelnosti. Nativní banner však není příliš viditelný a ovladatelný.
Zachycení události beforeinstallprompt můžete vytvořit zážitek
přizpůsobená instalace s vyhrazeným tlačítkem a designem konzistentním s vaší aplikací.
// pwa-install.component.ts
import {
Component, signal, inject, PLATFORM_ID, afterNextRender
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-pwa-install',
standalone: true,
template: `
<div class="install-banner">
<p>Installa la nostra app per un'esperienza migliore!</p>
<button (click)="installApp()">Installa</button>
<button (click)="dismissBanner()">Non ora</button>
</div>
`
})
export class PwaInstallComponent {
private platformId = inject(PLATFORM_ID);
showInstallBanner = signal(false);
isAppInstalled = signal(false);
private deferredPrompt: any = null;
constructor() {
afterNextRender(() => {
this.setupInstallPrompt();
this.checkIfInstalled();
});
}
private setupInstallPrompt(): void {
if (!isPlatformBrowser(this.platformId)) return;
// Intercetta l'evento beforeinstallprompt
window.addEventListener('beforeinstallprompt', (event: Event) => {
event.preventDefault();
this.deferredPrompt = event;
this.showInstallBanner.set(true);
});
// Rileva quando l'app viene installata
window.addEventListener('appinstalled', () => {
this.isAppInstalled.set(true);
this.showInstallBanner.set(false);
this.deferredPrompt = null;
console.log('PWA installata con successo!');
});
}
async installApp(): Promise<void> {
if (!this.deferredPrompt) return;
// Mostra il prompt di installazione nativo
this.deferredPrompt.prompt();
const result = await this.deferredPrompt.userChoice;
console.log('Scelta utente:', result.outcome);
if (result.outcome === 'accepted') {
console.log('Installazione accettata');
} else {
console.log('Installazione rifiutata');
}
this.deferredPrompt = null;
this.showInstallBanner.set(false);
}
dismissBanner(): void {
this.showInstallBanner.set(false);
}
private checkIfInstalled(): void {
// Verifica se l'app e già in modalità standalone
const isStandalone = window.matchMedia(
'(display-mode: standalone)'
).matches;
this.isAppInstalled.set(isStandalone);
}
}
Kritéria instalace PWA
| Požadavek | Popis |
|---|---|
| HTTPS | Aplikace musí být poskytována přes HTTPS (nebo localhost pro vývojáře) |
| Manifest webové aplikace | Musí obsahovat name, icons (192 + 512 pixelů), start_url, display |
| Servisní pracovníci | Aktivní servisní pracovník s obslužnou rutinou událostí fetch |
| Ještě není nainstalováno | Aplikace nemusí být již nainstalována na vašem zařízení |
| Uživatelská interakce | Uživatel musí mít interakci s doménou (kliknutí, posouvání atd.) |
10. Testování a ladění
Testování PWA vyžaduje specifické nástroje, protože servisní pracovník je pouze aktivní
ve výrobě a funkce PWA nejsou dostupné během vývoje s
ng serve. Chrome DevTools a Lighthouse poskytují vše, co potřebujete
testovat a ladit každý aspekt vašeho PWA.
Sestavení a místní služba pro testování
# 1. Build di produzione
ng build
# 2. Installa un server HTTP statico (una tantum)
npm install -g http-server
# 3. Servi la build di produzione
cd dist/tua-app/browser
http-server -p 8080 -c-1
# Oppure con npx (senza installazione globale)
npx serve dist/tua-app/browser -l 8080
# 4. Apri il browser su http://localhost:8080
# 5. Apri DevTools > Application tab
Chrome DevTools – karta Aplikace
Karta Aplikace Chrome DevTools je hlavním panelem kontrolovat a ladit všechny aspekty PWA.
Panely DevTools pro PWA
| Panel | Funkce | Ověřte |
|---|---|---|
| Manifest | Zobrazit analyzovaný manifest | Název, ikony, režim zobrazení, počáteční adresa URL jsou správné |
| Servisní pracovníci | Stav registrovaného SW | SW aktivní, čekající aktualizace, chyby |
| Vyrovnávací paměť | Obsah mezipaměti | Správné zdroje jsou v mezipaměti, celková velikost |
| IndexovanáDB | Lokální databáze | Offline data úspěšně přetrvávala |
| Skladování | Kvóta úložiště a využití | Využité versus dostupné místo |
Audit Lighthouse PWA
Lighthouse obsahuje část věnovanou ověřování PWA. Audit kontroluje kritéria instalovatelnost, offline optimalizace a osvědčené postupy. Dobře nakonfigurovaný PWA by měl získat skóre Lighthouse PWA 100/100.
Kontrolní seznam Lighthouse PWA
- Servisní pracovník zaregistruje handler
fetch - Stránka odpoví kódem 200 i offline
start_urlv manifestu a dosažitelné offline- Manifest má
nameoshort_name - Manifest má ikony o velikosti alespoň 192 a 512 pixelů
- Manifest specifikuje
display(samostatný/celá obrazovka/minimální uživatelské rozhraní) - Velikost obsahu je vhodná pro zobrazovanou oblast
- Meta tagy
theme-colorpřítomný v HTML - Stránka má a
viewportplatný meta tag - Web používá HTTPS
- HTTP je přesměrováno na HTTPS
Ladění Service Worker
// Nel browser, via DevTools Console:
// 1. Verificare lo stato del service worker
navigator.serviceWorker.getRegistrations().then(regs => {
regs.forEach(reg => {
console.log('SW Scope:', reg.scope);
console.log('SW State:', reg.active?.state);
console.log('SW Waiting:', reg.waiting);
console.log('SW Installing:', reg.installing);
});
});
// 2. Forzare l'aggiornamento del service worker
navigator.serviceWorker.getRegistrations().then(regs => {
regs.forEach(reg => reg.update());
});
// 3. Deregistrare tutti i service worker (per reset completo)
navigator.serviceWorker.getRegistrations().then(regs => {
regs.forEach(reg => reg.unregister());
});
// 4. Ispezionare la cache del service worker
caches.keys().then(names => {
names.forEach(name => {
caches.open(name).then(cache => {
cache.keys().then(requests => {
console.log(`Cache "${name}":`, requests.length, 'entries');
requests.forEach(req => console.log(' -', req.url));
});
});
});
});
// 5. Simulare stato offline via DevTools
// Network tab > seleziona "Offline" nel dropdown throttling
// Oppure: Application > Service Workers > spunta "Offline"
Běžné problémy a řešení
- SW se neaktualizuje: Během vývoje použijte "Aktualizovat při opětovném načtení" v DevTools > Aplikace > Service Workers
- Trvalá stará mezipaměť: Vymažte celé úložiště z DevTools > Aplikace > Úložiště > "Vymazat data webu"
- Nezobrazuje se výzva k instalaci: Ověřte, že jsou splněna všechna kritéria instalovatelnosti na panelu Manifest
- Push notifikace nepřicházejí: Zkontrolujte, zda je oprávnění „uděleno“.
Notification.permissiona že předplatné je platné - Chyby mezipaměti CORS: Externí zdroje musí být obsluhovány vhodnými hlavičkami CORS, aby je SW uložil do mezipaměti
- SW ve stavu "čekání": Nový SW čeká na zavření všech karet aplikace. USA
skipWaiting()vynutit okamžitou aktivaci
Shrnutí a další kroky
Progresivní webové aplikace představují přirozený vývoj webových aplikací, nabízí funkce, které byly ještě před několika lety exkluzivní pro nativní aplikace. Angular usnadňuje vytváření PWA díky vestavěné podpoře pro servisní pracovník, inteligentní ukládání do mezipaměti, upozornění push a spravované aktualizace.
Klíčové pojmy tohoto článku
- PWA: Instalovatelné webové aplikace s offline podporou, push notifikacemi a automatickými aktualizacemi
- Nastavení:
ng add @angular/pwageneruje manifest, konfiguraci SW a ikony - Manifest: Soubor JSON popisující aplikaci (název, ikony, režim zobrazení, počáteční adresa URL)
- ngsw-config.json: Konfigurace assetGroups (statické zdroje) a dataGroups (dynamická rozhraní API)
- Ukládání do mezipaměti: Strategie předběžného/opožděného načítání pro podklady, aktuálnost/výkon pro data API
- offline: Detekce stavu sítě, IndexedDB pro persistenci, synchronizace při opětovném připojení online
- TAM:
SwPushpro předplatné a přijímání oznámení, VAPID pro ověření serveru - Aktualizace:
SwUpdatekontrolovat, oznamovat a aktivovat nová vydání - Výzva k instalaci:
beforeinstallpromptpro uživatelské uživatelské rozhraní nastavení - Testování: Záložka Aplikace Chrome DevTools, audit Lighthouse PWA, ladění SW
V dalším článku série prozkoumáme Advanced Dependency Injection
v Angular, analyzování vlastních injekčních tokenů, poskytovatelů otřesitelných stromy,
multi-poskytovatel, tovární poskytovatel a strategie určování rozsahu providedIn.







