Progresyvios žiniatinklio programos su Angular

Kas iš tiesų yra Progressive Web Apps?

Prieš kelerius metus, kai Google pirmą kartą pradėjo kalbėti apie Progressive Web Apps (PWA), daugelis manė, kad tai tik dar vienas technologinis buzzword’as, kuris greitai išnyks. Na, pasirodo, ne. PWA technologija ne tik išliko, bet ir tapo rimtu žaidėju tarp mobilių aplikacijų kūrimo būdų.

Paprasčiausiai tariant, PWA yra web aplikacija, kuri veikia kaip native mobili aplikacija. Galite ją įsidiegti į savo telefoną tiesiai iš naršyklės, ji veikia offline, siunčia push pranešimus ir atrodo bei jaučiasi kaip tikra aplikacija. Bet iš esmės tai vis dar web puslapis, tik labai gerai paruoštas.

Kodėl tai svarbu? Nes jums nebereikia kurti atskirų aplikacijų iOS ir Android platformoms, mokėti Apple ar Google mokesčių už app store’us, ir laukti savaitėmis, kol jūsų atnaujinimai bus patvirtinti. Tiesiog atnaujinate savo web aplikaciją, ir visi naudotojai iš karto gauna naujausią versiją.

Angular kaip PWA kūrimo įrankis

Angular nėra vienintelis framework’as, su kuriuo galite kurti PWA, bet jis tikrai yra vienas geriausių. Google komanda į Angular įmontuoja PWA palaikymą iš dėžės, todėl jums nereikia visko kurti nuo nulio.

Kai pradėjau dirbti su Angular PWA, buvau maloniai nustebintas, kaip paprasta tai yra. Vietoj to, kad rankomis rašyčiau service worker’ius ir cache strategijas, Angular CLI suteikia komandą, kuri padaro didžiąją dalį darbo už mane:

ng add @angular/pwa

Ši viena komanda prideda visus reikalingus failus: manifest.json aplikacijos metaduomenims, service worker konfigūraciją, ikoniukus skirtingų dydžių, ir net atnaujina jūsų index.html su reikalingais meta tagais. Tai kaip turėti asmeninį PWA asistentuojantį, kuris žino, ką daro.

Žinoma, tai nereiškia, kad viskas veikia tobulai iš karto. Yra daug niuansų, kuriuos reikia suprasti, kad jūsų PWA būtų tikrai gera. Bet pradžia tikrai lengva.

Service Worker’iai – neregima PWA širdis

Service worker’iai yra tai, kas daro PWA tokias galinga. Tai JavaScript failas, kuris veikia fone, atskirai nuo jūsų pagrindinės aplikacijos, ir gali perimti tinklo užklausas, cache’inti turinį, ir net veikti, kai vartotojas nėra jūsų puslapyje.

Angular naudoja @angular/service-worker paketą, kuris suteikia labai galingą cache valdymo sistemą. Jūs galite konfigūruoti, kaip skirtingi resursai turėtų būti cache’inami per ngsw-config.json failą.

Štai tipinė konfigūracija, kurią naudoju daugelyje projektų:

{
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2)"
]
}
}],
"dataGroups": [{
"name": "api",
"urls": ["/api/**"],
"cacheConfig": {
"maxSize": 100,
"maxAge": "1h",
"strategy": "freshness"
}
}]
}

Šioje konfigūracijoje matote tris pagrindinius dalykus. Pirma, app grupė – tai jūsų pagrindiniai failai, kurie turi būti užkraunami iš karto (prefetch). Antra, assets grupė – paveikslėliai ir kiti statiniai resursai, kurie kraunami tik kai reikia (lazy). Trečia, dataGroups – tai API užklausų cache’inimas su strategija „freshness”, kuri visada bando gauti naujus duomenis, bet turi fallback į cache jei interneto nėra.

Offline funkcionalumas – kai internetas neveikia

Vienas didžiausių PWA privalumų yra galimybė veikti be interneto ryšio. Bet čia reikia būti protingam. Negalite tiesiog cache’inti visko ir tikėtis, kad viskas veiks.

Aš išmokau šį pamoką sunkiuoju būdu. Pirmame projekte su PWA, cache’inau visus API atsakymus be jokios logikos. Rezultatas? Vartotojai matė senas duomenis ir nesuprato, kodėl jų pakeitimai nedingsta. Buvo reikalinga aiški komunikacija su vartotoju apie tai, ar jie dirba online ar offline režime.

Angular suteikia SwUpdate servisą, kurį galite naudoti, kad stebėtumėte aplikacijos būseną:

import { SwUpdate } from '@angular/service-worker';

constructor(private swUpdate: SwUpdate) {
if (this.swUpdate.isEnabled) {
this.swUpdate.versionUpdates.subscribe(evt => {
if (evt.type === ‘VERSION_READY’) {
if (confirm(‘Nauja versija prieinama. Perkrauti?’)) {
window.location.reload();
}
}
});
}
}

Taip pat galite patikrinti, ar vartotojas yra online ar offline, naudojant paprastą window.navigator.onLine property, ir atitinkamai keisti UI. Pavyzdžiui, rodyti banner’į viršuje, kuris sako „Dirbate offline režime – jūsų pakeitimai bus sinchronizuoti kai prisijungsite”.

Dar vienas svarbus dalykas – background sync. Jei vartotojas atlieka kokį nors veiksmą offline (pvz., užpildo formą), galite išsaugoti tą veiksmą ir automatiškai jį išsiųsti, kai internetas vėl atsiras. Tai reikalauja šiek tiek daugiau darbo, bet vartotojų patirtis tampa neįtikėtinai gera.

Push pranešimai ir engagement

Push pranešimai yra viena iš tų funkcijų, kurios tikrai padidina vartotojų engagement’ą. Bet čia reikia būti atsargiam – niekas nemėgsta būti spaminamas pranešimais.

Angular PWA su push pranešimais veikia per Web Push API. Jums reikės backend’o, kuris galės siųsti pranešimus (paprastai naudojant Firebase Cloud Messaging arba panašų servisą), ir frontend’o kodo, kuris prašys leidimo ir užsiregistruos pranešimams.

Štai kaip aš paprastai implementuoju push pranešimų registraciją:

import { SwPush } from '@angular/service-worker';

constructor(private swPush: SwPush) {}

subscribeToPush() {
if (!this.swPush.isEnabled) {
console.log(‘Service Worker nepalaikomas’);
return;
}

this.swPush.requestSubscription({
serverPublicKey: ‘JŪSŲ_PUBLIC_KEY’
})
.then(subscription => {
// Išsiųskite subscription į jūsų backend’ą
this.sendToBackend(subscription);
})
.catch(err => {
console.error(‘Nepavyko užsiregistruoti’, err);
});
}

Svarbu: niekada neprašykite leidimo push pranešimams iš karto, kai vartotojas atidaro aplikaciją. Tai yra vienas greičiausių būdų vartotoją atstumt. Geriau palaukite, kol jie atliks kokį nors veiksmą, kuris rodo, kad jiems įdomu (pvz., užsiregistravo, pridėjo prekę į krepšelį, ir pan.), ir tik tada pasiūlykite įjungti pranešimus su aiškiu paaiškinimų, kokią naudą jie gaus.

Optimizavimas ir performance

PWA turi būti greita. Ne tiesiog greita, o tikrai greita. Vartotojai tikisi, kad aplikacija užsikraus per sekundę ar greičiau. Angular suteikia daug įrankių tam pasiekti, bet jums reikia juos teisingai naudoti.

Pirmas dalykas – lazy loading. Nekraukite viso aplikacijos kodo iš karto. Angular routing’as leidžia lengvai sukonfigūruoti lazy loading:

const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module')
.then(m => m.DashboardModule)
}
];

Antras dalykas – bundle size. Naudokite Angular CLI production build’ą, kuris automatiškai optimizuoja jūsų kodą:

ng build --configuration production

Šis build’as padaro tree-shaking’ą (pašalina nenaudojamą kodą), minifikuoja failus, ir įjungia ahead-of-time (AOT) kompiliavimą. Mano patirtis rodo, kad production build’as gali būti 50-70% mažesnis už development build’ą.

Trečias dalykas – image optimization. Paveikslėliai dažnai būna didžiausia aplikacijos dalis. Naudokite modernius formatus kaip WebP, lazy load’inkite paveikslėlius, kurie nėra matomi ekrane, ir visada nurodykite width ir height atributus, kad išvengtumėte layout shift’ų.

Angular 15 ir naujesni versijos turi NgOptimizedImage direktyvą, kuri daro daug šių dalykų automatiškai:

<img ngSrc="hero.jpg" width="400" height="300" priority>

Ketvirtas dalykas – stebėkite savo PWA performance su Lighthouse. Tai Chrome DevTools įrankis, kuris analizuoja jūsų aplikaciją ir duoda rekomendacijas. Siekite bent 90+ balų visose kategorijose.

Installability ir app-like patirtis

Kad jūsų PWA būtų tikrai „progressive”, ji turi būti įdiegiama. Tai reiškia, kad vartotojas gali pridėti ją į savo telefono home screen’ą ir naudoti kaip native aplikaciją.

Angular PWA automatiškai sukuria manifest.json failą, bet jums reikės jį customizuoti pagal savo poreikius:

{
"name": "Mano Super Aplikacija",
"short_name": "SuperApp",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
// ... daugiau ikonų
]
}

Svarbu turėti ikoniukas visų reikalingų dydžių. Angular PWA generator’ius sukuria daugumą jų, bet patikrinkite, ar turite bent 192×192 ir 512×512 dydžių ikoniukas. Taip pat naujesniuose Android įrenginiuose reikia „maskable” ikoniukų – tai ikoniukai, kurie gali būti apkarpyti į įvairias formas.

Display mode yra labai svarbus. „standalone” reiškia, kad aplikacija atsidarys be naršyklės UI (be address bar ir navigation buttons). Tai suteikia tikrą app-like jausmą. Kitos opcijos: „fullscreen” (visas ekranas), „minimal-ui” (minimalus naršyklės UI), ar „browser” (normalus naršyklės langas).

Dar vienas trikis – galite klausytis „beforeinstallprompt” evento ir rodyti savo custom install button’ą vietoj default naršyklės prompt’o:

let deferredPrompt;

window.addEventListener(‘beforeinstallprompt’, (e) => {
e.preventDefault();
deferredPrompt = e;
// Parodykite savo install button’ą
showInstallButton();
});

function installApp() {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === ‘accepted’) {
console.log(‘Vartotojas įdiegė aplikaciją’);
}
deferredPrompt = null;
});
}
}

Realūs iššūkiai ir kaip juos spręsti

Dirbant su Angular PWA, susidursite su įvairiais iššūkiais. Leiskite pasidalinti keliais, su kuriais aš susidūriau, ir kaip juos išsprendžiau.

Cache invalidation problema. Tai klasikinė problema – kaip žinoti, kada atnaujinti cache? Angular service worker’is turi built-in versioning sistemą, bet kartais to nepakanka. Aš paprastai pridėdu timestamp’ą arba version number’į prie API užklausų, kad galėčiau kontroliuoti, kada cache turėtų būti atnaujintas.

iOS Safari quirks. Apple nėra didžiausias PWA fanas, ir Safari turi daug apribojimų. Pavyzdžiui, cache limitas yra tik 50MB, o push pranešimai iOS Safari neveikia (bent jau iki iOS 16.4). Sprendimas? Turėkite fallback strategijas ir aiškiai komunikuokite vartotojams apie funkcionalumo skirtumus.

Service worker update’ai. Kartais service worker’is „užstringa” senoje versijoje ir nesiatnaujina. Aš visada įtraukiu mechanizmą, kuris priverstinai perkrauna aplikaciją, kai aptinkama nauja versija. Taip pat galite naudoti „skipWaiting” strategiją, kuri iš karto aktyvuoja naują service worker’į.

Debugging. Service worker’ių debug’inimas gali būti sudėtingas, nes jie veikia fone. Chrome DevTools turi puikią „Application” tab’ą, kur galite matyti visą service worker’io būseną, cache turinį, ir net simuliuoti offline režimą. Naudokite tai intensyviai development metu.

HTTPS reikalavimas. PWA veikia tik per HTTPS (išskyrus localhost). Jei jūsų aplikacija dar neturi SSL sertifikato, gaukite jį. Let’s Encrypt suteikia nemokamus sertifikatus, ir daugelis hosting providerių juos palaiko automatiškai.

Kai viskas susiglaudžia kartu

Progressive Web Apps su Angular nėra raketų mokslas, bet reikalauja dėmesio detalėms. Jūs gaunate web aplikacijos lankstumą ir native aplikacijos galimybes – tai geriausias iš abiejų pasaulių.

Pradėkite paprastai. Naudokite ng add @angular/pwa, patikrinkite, ar viskas veikia, ir palaipsniui pridėkite daugiau funkcionalumo. Optimizuokite performance, įsitikinkite, kad offline režimas veikia gerai, ir nepamirškite testuoti įvairiuose įrenginiuose ir naršyklėse.

Svarbiausia – galvokite apie vartotojo patirtį. PWA technologija yra tik įrankis. Jūsų tikslas – sukurti aplikaciją, kuri yra greita, patikima, ir maloni naudoti. Jei tai pasieksite, vartotojai net nepastebės, kad naudoja web aplikaciją, o ne native – ir tai yra aukščiausias komplimentas, kurį galite gauti.

Taigi, jei dar nesate bandę kurti PWA su Angular, dabar puikus laikas pradėti. Framework’as yra brandus, įrankiai yra geri, ir community palaikymas yra stiprus. O jei jau dirbate su Angular, pridėti PWA funkcionalumą prie egzistuojančios aplikacijos yra paprasčiau, nei galvojate.

Daugiau

Selenium WebDriver: naršyklės automatizavimas

Apache Spark ar Databricks