Hoppa till innehåll

Progressiva webbapplikationer

Allt du har byggt sedan kursvecka 8 körs i en flik i webbläsaren. Den här veckan tar du steget mot applikationer som går att installera och som fungerar utan uppkoppling, progressiva webbapplikationer. Det är också vad som krävs för högre betyg på examinationsuppgift B3, där skrivbordet du bygger ska gå att installera och fungera offline.

Den här artikeln går igenom begreppen och hur ett manifest gör applikationen installerbar. De två följande artiklarna, Service workers och Offline med Cache API, visar hur den fungerar offline.

Tidsåtgång: Ungefär 12 minuter.

En traditionell webbplats består av många HTML-dokument. Varje klick på en länk hämtar ett nytt dokument från servern, och webbläsaren ritar upp sidan från början. En sådan webbplats kallas ibland för en MPA, en multi-page application.

En SPA, en single-page application, laddar ett enda HTML-dokument. Därefter är det JavaScript som ändrar DOM:en när användaren gör något, och hämtar data med fetch() eller över en WebSocket när det behövs. Sidan laddas aldrig om. Det är så alla dina applikationer i delkurs B har fungerat: quizet i B2 byter mellan fråga och topplista utan att ladda ett nytt dokument, och B3 ska vara en SPA enligt kraven.

Att sidan aldrig laddas om har två följder som är värda att känna till:

  • Tillståndet finns i JavaScript. Vilka fönster som är öppna, var de står och vad som har skrivits i dem finns i variabler och i DOM:en, och försvinner när sidan laddas om.
  • Adressen ändras inte av sig själv. Användaren hamnar alltid på samma adress, och bakåtknappen lämnar applikationen. Hur en SPA kan uppdatera adressen tar du upp i Routing i en SPA i kursvecka 16.

Användaren har tre fönster öppna i ditt PWD: ett Memory mitt i en omgång, ett Messages-fönster och din egen underapplikation. Sedan trycker användaren på F5. Vad finns kvar?

Ett tomt skrivbord. Sidan laddas om, all JavaScript körs från början och alla variabler och element skapas på nytt. Omgången i Memory är borta, liksom fönstrens placering.

Det som har sparats överlever: användarnamnet i Messages, om du har sparat det i localStorage som kraven säger, och de meddelanden servern skickar när du ansluter på nytt. Att spara fönstrens läge mellan sessioner är ett av förslagen på utökningar i B3, och du har redan verktyget för det från Web Storage.

Ska en applikation finnas på telefonen och på datorn finns i stort sett fyra vägar att gå:

  • Native app. Skrivs för en plattform i dess eget språk och med dess egna verktyg, Swift för iOS och Kotlin för Android. Ger full tillgång till enheten och bäst prestanda, men en kodbas per plattform och installation via appbutiken.
  • Plattformsoberoende app. En kodbas som byggs till appar för flera plattformar. Tekniken skiljer sig åt: Capacitor och Cordova kör en webbapplikation i en webview, en inbyggd webbläsare utan adressfält, inuti en native app. React Native kör JavaScript men ritar plattformens egna gränssnittselement. Flutter skrivs i Dart, kompileras till maskinkod och ritar hela gränssnittet själv.
  • Responsiv webbplats. Anpassar sig efter skärmen, men lever i en flik i webbläsaren och fungerar inte utan uppkoppling.
  • Progressiv webbapplikation. En webbapplikation som går att installera och som fungerar offline. En kodbas, ingen appbutik, och en uppdatering når alla användare nästa gång de öppnar den.

På datorn finns en motsvarighet till webview-appen. Electron paketerar en webbapplikation tillsammans med en egen Chromium och Node.js till ett skrivbordsprogram. VS Code, Slack och Discord är byggda så. Tauri gör samma sak men använder operativsystemets inbyggda webview i stället för att skicka med en egen webbläsare. Det är webbteknik, men inte en PWA: programmet installeras som vilket program som helst, inte från webbläsaren.

Native Plattformsoberoende Responsiv webbplats PWA
Kodbaser En per plattform En En En
Installeras från Appbutik Appbutik Installeras inte Webbläsaren
Fungerar offline Ja Ja Nej Ja
Tillgång till enheten All Det mesta Webbläsarens API:er Webbläsarens API:er
Nås via en länk Nej Nej Ja Ja
Uppdatering Via appbutiken Via appbutiken Direkt Direkt

Inget av alternativen är bäst i allmänhet. En PWA är ett bra val när webbläsarens API:er räcker, och de räcker långt: kamera, mikrofon, position, notiser, filer, Bluetooth och WebGL finns alla i webbläsaren, även om stödet skiljer sig mellan webbläsare.

Begreppet progressive web app myntades 2015 av Frances Berriman och Alex Russell, då på Google. Det är inte en enskild teknik utan en samling egenskaper. Tre brukar lyftas fram:

  • Installerbar. Användaren kan installera applikationen från webbläsaren. Den startar sedan i ett eget fönster, med egen ikon, som andra program. Det kräver ett manifest, som du ser nedan.
  • Pålitlig. Applikationen startar och fungerar även när nätverket är långsamt eller saknas helt. Det kräver en service worker, som är ämnet för de två följande artiklarna.
  • Kapabel. Applikationen använder det webbläsaren kan, och gör saker användaren annars hade installerat en native app för.

Ordet progressiv kommer från progressive enhancement, progressiv förbättring. Applikationen ska fungera som en vanlig webbsida i varje webbläsare, och bli bättre där webbläsaren kan mer. Därför kontrollerar du att ett API finns innan du använder det, i stället för att anta det:

if ('serviceWorker' in navigator) {
// Register the service worker
}
if (navigator.share) {
// Show a share button
}

Saknas stödet fungerar applikationen ändå, bara utan just den förbättringen.

Den som bygger pålitliga applikationer behöver tänka om kring nätverket. I en vanlig webbplats är uppkoppling något som tas för givet, och ett avbrott är ett fel. Offline first vänder på det: uppkopplingen är något som kan finnas, och applikationen ska fungera så långt det går utan den. Memory behöver inget nätverk alls efter att filerna har hämtats en gång. Messages behöver nätverket för att skicka och ta emot meddelanden, men kan fortfarande starta och tala om för användaren att den är offline, i stället för att visa webbläsarens felsida.

Hur applikationen märker att den är offline, och varför navigator.onLine inte räcker, tar du upp i Online, offline och Lie-Fi i kursvecka 16.

Ett manifest gör applikationen installerbar

Section titled “Ett manifest gör applikationen installerbar”

Manifestet är en JSON-fil som beskriver applikationen för operativsystemet: vad den heter, vilka ikoner den har och hur den ska visas när den är installerad. Ett manifest för memoryspelet:

{
"name": "Memory Game",
"short_name": "Memory",
"description": "A memory game with flipping tiles.",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffe000",
"icons": [
{ "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
  • name och short_name är namnet i fönstrets namnlist och under ikonen, där det korta används när utrymmet är litet.
  • start_url är adressen som öppnas när applikationen startas.
  • display bestämmer hur mycket av webbläsaren som syns. standalone ger ett eget fönster utan adressfält och flikar, som ett vanligt program. minimal-ui behåller några navigeringsknappar, fullscreen tar hela skärmen och browser öppnar en vanlig flik.
  • background_color och theme_color används för startskärmen och fönstrets ram, innan din CSS har laddats.
  • icons är ikonerna i olika storlekar.

Relativa adresser i manifestet, som ./ och icons/icon-192.png, räknas från manifestets egen adress, inte från sidans.

I ett Vite-projekt lägger du manifestet och ikonerna i public/. Vite kopierar allt där som det är, med samma namn, medan filer som importeras från din kod får en hash i namnet vid bygget, som index-DpQX1ODm.js. Manifestet pekar ut ikonerna med namn, och då måste namnen vara desamma efter bygget.

public/
├── favicon.ico
├── manifest.webmanifest
└── icons/
├── icon-192.png
└── icon-512.png

Sedan länkar du in manifestet i src/index.html, bredvid ikonen som redan finns där:

<link rel="icon" href="./favicon.ico" />
<link rel="manifest" href="./manifest.webmanifest" />

Kraven skiljer sig mellan webbläsare. Chrome och Edge, som båda bygger på Chromium, kräver:

  • att sidan visas i en säker kontext, alltså över HTTPS eller från localhost,
  • ett manifest med name eller short_name, start_url, display satt till standalone, fullscreen eller minimal-ui, och ikoner i storlekarna 192 × 192 och 512 × 512 pixlar.

En service worker krävs inte längre för att installera via webbläsarens meny, sedan Chrome 108 på mobilen och 112 på datorn. Utan den startar applikationen, men visar webbläsarens felsida när nätverket saknas. Det är först med en service worker som den blir pålitlig.

Stödet i övrigt:

  • Chrome och Edge installerar på Windows, macOS, Linux och Android.
  • Safari på macOS (sedan version 17) lägger till vilken webbplats som helst i Dock via Arkiv → Lägg till i Dock. På iOS och iPadOS (sedan 16.4) installeras den via delningsmenyn.
  • Firefox installerar inte utifrån manifestet. Sedan version 143 kan en webbplats fästas som app i aktivitetsfältet i Windows.

Testa därför installationen i Chrome eller Edge. Under fliken Application → Manifest i utvecklarverktygen ser du hur webbläsaren har tolkat manifestet och om något saknas.

Fråga: varför går den inte att installera?

Section titled “Fråga: varför går den inte att installera?”

Du har lagt till ett manifest med name, start_url, display: "standalone" och en ikon på 192 × 192 pixlar. Du kör npm run build och npm run serve, öppnar http://localhost:4173 i Chrome, men webbläsaren erbjuder ingen installation. Vad är fel?

Ikonen på 512 × 512 pixlar saknas. Chromium kräver båda storlekarna, och utan den är applikationen inte installerbar. Fliken Application → Manifest i utvecklarverktygen visar vilket krav som inte är uppfyllt.

localhost är inte problemet. Det räknas som en säker kontext, precis som HTTPS, så att du kan utveckla utan certifikat.

  • En SPA laddar ett dokument och låter JavaScript sköta resten. Tillståndet finns i minnet och försvinner när sidan laddas om, om du inte sparar det.
  • Native, plattformsoberoende, responsiv webbplats och PWA är fyra sätt att bygga en app, med olika avvägningar mellan räckvidd, tillgång till enheten och hur uppdateringar når användaren.
  • En PWA är installerbar, pålitlig och kapabel. Progressiv betyder att den fungerar överallt och blir bättre där webbläsaren kan mer.
  • Ett manifest i public/, länkat från index.html, gör applikationen installerbar. Chromium kräver ikoner i 192 och 512 pixlar.
  • Installerbar är inte samma sak som pålitlig. Det kräver en service worker.
CCBY