Angular + PWA: Yüklenebilir Bir Uygulama Oluşturun
Le Aşamalı Web Uygulamaları (PWA) web uygulamaları arasındaki köprüyü temsil ederler ve yerel uygulamalar. Benim gibi teknolojiler sayesinde Hizmet Çalışanları, il Web Uygulaması Bildirimi ve Anında Bildirim, bir PWA teklifi Mağazadan yüklenen bir uygulamanın deneyimine benzer deneyimler, ancak maliyet ve yerel dağıtımın karmaşıklığı. Angular, PWA desteğini yerel olarak entegre eder, Bir web uygulamasını yüklenebilir bir uygulamaya dönüştürmeyi bir süreç haline getirmek basit ve iyi belgelenmiş.
Serinin bu sekizinci makalesinde Modern Açısal her yönünü keşfedeceğiz Angular ile PWA oluşturmanın yolları: ilk konfigürasyondan gelişmiş yönetime kadar çevrimdışı destekten anlık bildirimlere ve güncelleme mekanizmalarına kadar önbellek otomatik ve özel yükleme istemi. Her birinin nasıl test edileceğine ve hata ayıklanacağına bakacağız Chrome DevTools araçlarıyla işlevsellik.
Bu Makalede Neler Öğreneceksiniz?
- PWA'lar nelerdir ve yerel uygulamalara göre ne gibi avantajlar sunarlar?
- Angular PWA ile nasıl kurulur
ng add @angular/pwa - Simgeler, renkler ve görüntüleme modları için Web Uygulaması Bildirimini özelleştirme
- Service Worker'ı yapılandırma
ngsw-config.json - Önbelleğe alma stratejileri: önceden getirme, tembel, önce ağ, önce önbellek
- Yedek sayfalar ve IndexedDB ile çevrimdışı destek
- Hizmetle anında bildirim
SwPush - Güncellemeleri şununla yönet:
SwUpdate - Özel istemi şununla yükleyin:
beforeinstallprompt - Chrome DevTools ve Lighthouse ile test etme ve hata ayıklama
Modern Açısal Serilere Genel Bakış
| # | Öğe | Odak |
|---|---|---|
| 1 | Açısal Sinyaller | İnce taneli yanıt verme |
| 2 | Bölgesiz Değişim Tespiti | Zone.js'yi silin |
| 3 | Yeni Şablonlar @if, @for, @defer | Modern kontrol akışı |
| 4 | Bağımsız Bileşenler | NgModule'suz mimari |
| 5 | Sinyal Formları | Sinyallerle duyarlı formlar |
| 6 | SSR ve Artan Hidrasyon | Sunucu Tarafı İşleme |
| 7 | Angular'da Temel Web Verileri | Performans ve ölçümler |
| 8 | Buradasınız - Açısal PWA | Aşamalı Web Uygulamaları |
| 9 | Gelişmiş Bağımlılık Ekleme | DI ağacı sarsılabilir |
| 10 | Angular 17'den 21'e geçiş | Geçiş kılavuzu |
1. Aşamalı Web Uygulamaları Nelerdir?
Una Aşamalı Web Uygulamaları teknolojileri kullanan bir web uygulamasıdır Yerel bir uygulamayla karşılaştırılabilir bir kullanıcı deneyimi sunmak için modern. Terim "aşamalı", uygulamanın tarayıcıdan bağımsız olarak her kullanıcı için çalıştığı anlamına gelir destekleyen tarayıcılardaki deneyimi giderek geliştirerek, gelişmiş özellikler.
PWA'ların Temel Özellikleri
PWA'lar geleneksel web uygulamalarından bir dizi yetenekle ayrılır. onları mağazalar aracılığıyla dağıtılan yerel uygulamalara yaklaştırıyorlar.
PWA, Yerel Uygulama ve Geleneksel Web Uygulaması
| karakteristik | Web Uygulaması | PWA | Yerel Uygulama |
|---|---|---|---|
| Kurulabilir | No | Si | Si |
| Çevrimdışı çalışır | No | Si | Si |
| Anında bildirim | No | Si | Si |
| Otomatik güncelleme | Si | Si | Mağaza aracılığıyla |
| Donanım erişimi | Sınırlı | İyi | Tamamlamak |
| Dağıtım | URL'si | URL + Kurulum | Uygulama Mağazası |
| Geliştirme maliyeti | Bas | Bas | Yüksek |
| SEO | Si | Si | Hayır (mağaza indeksleme) |
Ne Zaman PWA Seçilmeli?
En fazla sayıda kullanıcıya ulaşmak istediğinizde PWA'lar ideal seçimdir. tek bir kod tabanı. E-ticaret, bloglar, şirket gösterge tabloları ve araçlar için mükemmeldirler üretkenlik ve çevrimdışı erişim ve bildirimlerden yararlanan tüm uygulamalar itin. Uygulama donanıma derinlemesine erişim gerektiriyorsa (gelişmiş Bluetooth, NFC, sensörler) özellikler), yerel bir uygulama yine de gerekli olabilir.
2. Açısal PWA Kurulumu
Angular, PWA yapılandırmasını otomatikleştiren resmi bir şema sağlar. bir ile Tek komutla CLI gerekli tüm dosyaları oluşturur: manifest, yapılandırma servis çalışanı ve varsayılan simgeler.
# 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)
app.config.ts'de yapılandırma
Şemayı çalıştırdıktan sonra, app.config.ts güncellendi
Servis çalışanını otomatik olarak kaydetmek için. Kayıt yalnızca
üretim ve uygulama sonrası stabilizasyon.
// 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'
})
]
};
Hizmet Çalışanı ve Geliştirme Ortamı
Hizmet çalışanı geliştirme modunda devre dışı bırakıldı (isDevMode()) neden
agresif önbelleğe alma, çalışırken yeniden yüklemeyi ve canlı yeniden yüklemeyi engelleyecektir. Test etmek
yerel olarak servis çalışanını kullanarak bir üretim yapısı çalıştırmanız gerekir
ng build ve statik dosyaları aşağıdaki gibi bir HTTP sunucusuyla sunun
http-server o npx serve.
3. Web Uygulaması Bildirimi
Il Web Uygulaması Bildirimi başvurunuzu açıklayan bir JSON dosyasıdır tarayıcı ve işletim sistemi. Uygulama adı, simgeler gibi bilgileri içerir. tema renkleri ve görüntüleme modu. Geçerli bir bildirim olmadan tarayıcı uygulamayı yükleme olanağı sunmuyor.
{
"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" }]
}
]
}
Manifest Görüntüleme Modu
| Mod | Tanım | Kullanım Örneği |
|---|---|---|
standalone |
Uygulama, adres çubuğu olmayan yerel bir uygulama olarak görünüyor | PWA için en yaygın seçim |
fullscreen |
Uygulama, tarayıcı arayüzü olmadan ekranın tamamını kaplıyor | Oyunlar, sürükleyici sunumlar |
minimal-ui |
Bağımsıza benzer ancak minimum gezinme kontrolüne sahiptir | Geri/ileri gezinme gerektiren uygulamalar |
browser |
Uygulama standart bir tarayıcı sekmesinde açılır | Geleneksel web siteleri (PWA için önerilmez) |
4. Hizmet Çalışanını Yapılandırma
Dosya ngsw-config.json Angular'daki PWA konfigürasyonunun kalbidir.
Hangi kaynakların önbelleğe alınacağını, nasıl güncelleneceğini ve hangi kaynaklarla güncelleneceğini tanımlar
strateji. Angular dosyayı otomatik olarak oluşturur ngsw-worker.js sırasında
üretim yapısı bu konfigürasyona dayanmaktadır.
{
"$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 ve dataGroups karşılaştırması
| Mülk | varlıkGrupları | veriGrupları |
|---|---|---|
| Kapsam | Statik uygulama kaynakları (JS, CSS, resimler) | API'den dinamik veriler |
| Sürüm oluşturma | Dosya karmasını temel alır | Zamana dayalı (maxAge) |
| kurulumModu | prefetch o lazy |
Yok |
| strateji | Yok | freshness o performance |
| Güncelleme | Uygulama sürümü değiştiğinde | MaxAge ve timeout'a göre |
5. Önbelleğe Alma Stratejileri
Önbelleğe alma stratejisinin seçimi performansı dengelemek için önemlidir.
veri tazeliği. Angular NGSW, i için iki ana stratejiyi destekler
dataGroups: tazelik (önce ağ) e
performans (önce önbellek) ve varlıklar için statik önbelleğe alma.
Ayrıntılı Olarak Önbelleğe Alma Stratejileri
| Strateji | Öncelik | Davranış | Kullanım Örneği |
|---|---|---|---|
| önceden getir (varlıkGrupları) | Önbellek | SW'nin kurulumundan sonra tüm kaynakları hemen indirin | Kabuk uygulamaları, temel JS ve CSS |
| tembel (varlıkGrupları) | İsteğe bağlı önbelleğe alma | Kaynağı yalnızca istendiğinde önbelleğe alır | Resimler, yazı tipleri, ikincil varlıklar |
| tazelik (veriGrupları) | Ağ | Ağı deneyin; zaman aşımı süresi dolmuşsa önbelleği kullanın | Kullanıcı profili, bildirimler, gerçek zamanlı veriler |
| performans (veriGrupları) | Önbellek | Varsa ve süresi dolmamışsa önbelleği kullanın; aksi halde ağ | Blog makaleleri, ürün kataloğu, statik veriler |
Strateji tazelik desene eşdeğerdir ağ öncelikli: hizmet çalışanı önce ağı dener ve eğer istek zaman aşımı süresi içinde başarısız olursa belirtildiğinde, önbelleğe alınmış sürüm gereklidir. Strateji performans öyle bunun yerine önbellek öncelikli: hemen önbelleğe alınmış sürüme ihtiyacınız var (süresi dolmamışsa) ve arka planda önbelleği yenileyin.
Eskimişken Angular ile Yeniden Doğrulama
Angular NGSW stratejiyi yerel olarak desteklemiyor yeniden doğrulama sırasında eskimiş,
ancak stratejiyi birleştirerek bunu simüle edebilirsiniz performans bir ile
maxAge kısa bilgi. Örneğin, ayarlama "maxAge": "5m" önbellek
verileri hemen sunacak, ancak 5 dakika sonra ağa yeni bir istekte bulunacak
bir sonraki girişte. Daha ayrıntılı bir kontrole ihtiyacınız varsa bunu kullanmayı düşünün
doğrudan Önbellek API'si özel bir servis çalışanı ile.
6. Çevrimdışı Destek
PWA'nın en önemli özelliklerinden biri, PWA olmadan çalışabilme yeteneğidir. İnternet bağlantısı. Angular, kaynak önbelleğe almayı otomatik olarak yönetir hizmet çalışanı aracılığıyla statiktir ancak tam bir çevrimdışı deneyim için bu gereklidir ayrıca bağlantı durumunu, yedek sayfaları ve sunucu kalıcılığını da yönetin yerel veriler.
Bağlantı Durumu Tespiti
// 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
}
}
IndexedDB ile Veri Kalıcılığı
Yapılandırılmış verileri çevrimdışı yönetmek için, IndexedDB en sağlam çözümdür.
Farklı localStorage (senkron, limit 5-10MB), IndexedDB destekler
büyük hacimli veriler, dizine alınmış sorgular ve eşzamansız işlemler.
// 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);
});
}
}
Çevrimdışı Depolamanın Sınırlamaları
- Tarayıcı paylaşımı: Tarayıcılar kullanılabilir depolama alanını sınırlar (genellikle disk alanının %50-80'i). Amerika
navigator.storage.estimate()kullanılabilir alanı kontrol etmek için - Tahliye politikası: Yeterli alan yoksa tarayıcı verileri silebilir. İle kalıcı depolama isteğinde bulunun
navigator.storage.persist() - SSR güvenliği: IndexedDB sunucu tarafında mevcut değil. Her zaman kullan
isPlatformBrowsererişmeden önce - Senkronizasyon çakışmaları: Kullanıcı tekrar çevrimiçi olduğunda yerel veriler sunucu verileriyle çakışabilir. Bir çatışma çözümü stratejisi uygulayın
7. Anında Bildirim
Anında bildirimler, PWA'nızın kullanıcıya mesaj göndermesine olanak tanır.
uygulama aktif değil. Angular hizmeti sağlıyor SwPush bu basitleştirir
aboneliklerinizi yönetmek ve bildirimleri almak için
Web Push Protokolü.
Anlık Bildirimlere Abonelik
// 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);
}
}
}
Sunucudan Bildirim Gönderme
// 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);
}
}
}
VAPID Anahtarları
VAPID anahtarları (Gönüllü Uygulama Sunucusu Tanımlaması) için kullanılır
sunucunuzun kimliğini tarayıcı push hizmetine doğrulayın. Bunları şununla oluşturabilirsiniz:
komut npx web-push generate-vapid-keys. Genel anahtar girilmelidir
ön uçta özel olan yalnızca sunucuda kalır. Asla taahhüt etmeyin
depodaki özel anahtar.
8. Güncelleme Yönetimi
PWA'nızın yeni bir sürümünü yayınladığınızda, hizmet çalışanının bu sürümü indirmesi gerekir.
kaynakları güncelleyin ve kullanıcıyı bilgilendirin. Angular hizmeti sağlıyor
SwUpdate bir şekilde güncelleme yaşam döngüsünü yönetmek
kontrol edildi.
// 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();
});
}
}
}
PWA Güncellemesinin Yaşam Döngüsü
| Faz | SwUpdate etkinliği | Tanım |
|---|---|---|
| 1. Tespit | VERSION_DETECTED |
SW sunucuda yeni bir sürüm buldu |
| 2. İndirilenler | VERSION_INSTALLATION_FAILED |
Yeni sürüm indirilirken hata oluştu (başarısız olursa) |
| 3. Hazır | VERSION_READY |
Yeni sürüm indirildi ve aktivasyona hazır |
| 4. Etkinleştirme | activateUpdate() |
Kullanıcı onaylar ve SW yeni sürümü etkinleştirir |
| 5. Şarj Edin | location.reload() |
Sayfa güncellenen kaynaklarla yeniden yüklenir |
Kurtarılamaz Hizmet Çalışanı Hatası
Etkinlik unrecoverable servis çalışanının artık bunu yapamayacağını gösterir
uygulamanın geçerli sürümünü doğru bir şekilde sunmak (örneğin, sunucu kaldırılmış)
kaynaklara hala ihtiyaç duyulmaktadır). Bu durumda tek çözüm yeniden yüklemeyi zorlamaktır.
sayfa ile tamamlayın document.location.reload(). Bu olayı izle
Dağıtım sorunlarını belirlemek için üretimde.
9. Özel Kurulum İstemi
PWA gereksinimleri karşıladığında tarayıcılar otomatik olarak bir kurulum başlığı görüntüler
kurulabilirlik kriterleri. Ancak yerel banner pek görünür ve kontrol edilebilir değildir.
Olayın durdurulması beforeinstallprompt bir deneyim yaratabilirsiniz
özel bir düğme ve uygulamanızla tutarlı bir tasarımla özelleştirilmiş kurulum.
// 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);
}
}
PWA Yüklenebilirlik Kriterleri
| Gereklilik | Tanım |
|---|---|
| HTTPS | Uygulama HTTPS (veya geliştirme için localhost) üzerinden sunulmalıdır |
| Web Uygulaması Bildirimi | Dahil edilmelidir name, icons (192 piksel + 512 piksel), start_url, display |
| Hizmet Çalışanları | Olay işleyicisine sahip etkin bir hizmet çalışanı fetch |
| Zaten yüklü değil | Uygulamanın cihazınızda önceden kurulu olması gerekmez |
| Kullanıcı etkileşimi | Kullanıcının alan adı ile etkileşime girmiş olması gerekir (tıklama, kaydırma vb.) |
10. Test Etme ve Hata Ayıklama
Hizmet çalışanı yalnızca etkin olduğundan PWA'yı test etmek için özel araçlar gerekir
üretimde ve PWA özellikleri geliştirme sırasında mevcut değildir
ng serve. Chrome DevTools ve Lighthouse ihtiyacınız olan her şeyi sağlar
PWA'nızın her yönünü test etmek ve hata ayıklamak için.
Test için Derleme ve Yerel Hizmet
# 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 Geliştirici Araçları - Uygulama Sekmesi
Sekme Başvuru Chrome DevTools'un ana paneli PWA'nın tüm yönlerini inceleyin ve hata ayıklayın.
PWA için DevTools panelleri
| Paneli | İşlev | Doğrulamak |
|---|---|---|
| manifest | Ayrıştırılmış bildirimi göster | Ad, simgeler, görüntüleme modu, başlangıç URL'si doğru |
| Hizmet Çalışanları | Kayıtlı SW'nin durumu | Yazılım etkin, güncellemeler bekleniyor, hatalar |
| Önbellek Depolama | Önbellek içeriği | Doğru kaynaklar önbellekte, toplam boyutta |
| İndekslenmişDB | Yerel veritabanları | Çevrimdışı veriler başarıyla sürdürüldü |
| Depolamak | Depolama kotası ve kullanımı | Kullanılan ve kullanılabilir alan karşılaştırması |
Lighthouse PWA Denetimi
Lighthouse, PWA doğrulamasına ayrılmış bir bölüm içerir. Denetim kriterleri kontrol eder kurulabilirlik, çevrimdışı optimizasyonlar ve en iyi uygulamalar. İyi yapılandırılmış bir PWA Lighthouse PWA puanı almalı 100/100.
Deniz Feneri PWA Kontrol Listesi
- Hizmet çalışanı bir işleyiciyi kaydeder
fetch - Sayfa çevrimdışıyken bile 200 koduyla yanıt veriyor
start_urlbildirimde ve çevrimdışı erişilebilir durumda- Manifest var
nameoshort_name - Manifest'te en az 192 piksel ve 512 piksel boyutunda simgeler bulunur
- Manifesto şunu belirtir:
display(bağımsız/tam ekran/minimum kullanıcı arayüzü) - İçerik görünüm için uygun şekilde boyutlandırılmıştır
- Meta etiketleri
theme-colorHTML'de mevcut - Sayfada bir
viewportgeçerli meta etiketi - Site HTTPS kullanıyor
- HTTP, HTTPS'ye yönlendirilir
Hizmet Çalışanı Hata Ayıklama
// 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"
Yaygın Sorunlar ve Çözümler
- SW güncellenmiyor: Geliştirme sırasında DevTools > Uygulama > Hizmet Çalışanları'nda "Yeniden yüklemede güncelleme"yi kullanın
- Kalıcı eski önbellek: DevTools > Uygulama > Depolama > "Site verilerini temizle" bölümünden depolamanın tamamını temizleyin
- Kurulum istemi görünmüyor: Manifest panelinde tüm kurulabilirlik kriterlerinin karşılandığını doğrulayın
- Anlık bildirimler gelmiyor: İznin "verildiğini" kontrol edin
Notification.permissionve aboneliğin geçerli olduğunu - CORS önbellek hataları: Harici kaynakların, SW tarafından önbelleğe alınması için uygun CORS başlıklarıyla sunulması gerekir
- SW "bekleme" durumunda: Yeni SW, tüm uygulama sekmelerinin kapatılmasını bekler. Amerika
skipWaiting()anında aktivasyonu zorlamak için
Özet ve Sonraki Adımlar
Progresif Web Uygulamaları, web uygulamalarının doğal bir evrimini temsil eder, birkaç yıl öncesine kadar yerel uygulamalara özel olan özellikler sunuyor. Angular, yerleşik destek sayesinde PWA'lar oluşturmayı çok daha kolay hale getiriyor hizmet çalışanı, akıllı önbelleğe alma, anında bildirim ve yönetilen güncellemeler.
Bu Makalenin Temel Kavramları
- PWA: Çevrimdışı destek, anında bildirimler ve otomatik güncellemeler sunan kurulabilir web uygulamaları
- Kurmak:
ng add @angular/pwamanifest, SW konfigürasyonu ve simgeler oluşturur - Manifesto: Uygulamayı açıklayan JSON dosyası (ad, simgeler, görüntüleme modu, başlangıç URL'si)
- ngsw-config.json: assetGroup'ları (statik kaynaklar) ve dataGroup'ları (dinamik API'ler) yapılandırma
- Önbelleğe alma: Varlıklar için önceden getirme/tembel stratejiler, API verileri için tazelik/performans
- Çevrimdışı: Ağ durumu tespiti, kalıcılık için IndexedDB, tekrar çevrimiçi olduğunda senkronizasyon
- İtmek:
SwPushabonelikler ve bildirim alma için, sunucu kimlik doğrulaması için VAPID - Güncellemeler:
SwUpdateyeni sürümleri kontrol etmek, bilgilendirmek ve etkinleştirmek için - İstemi yükleyin:
beforeinstallpromptözel kurulum kullanıcı arayüzü için - Test: Chrome DevTools Uygulama sekmesi, Lighthouse PWA denetimi, Yazılım hata ayıklaması
Serinin bir sonraki makalesinde bu konuyu inceleyeceğiz. Gelişmiş Bağımlılık Ekleme
Angular'da, özel enjeksiyon belirteçlerini, sarsılabilir sağlayıcıları analiz etmek,
çoklu sağlayıcı, fabrika sağlayıcı ve kapsam belirleme stratejileri ile providedIn.







