Hoppa till innehållet

Progressive web app

en webbplats som kan installeras och köras som en app

  • Ships, sea, sunset at sea
    Foto: Andrey_and_Olesya / Pixabay
  • Charging phone, power bank, charger
    Foto: C10Maj / Pixabay

Använd begreppet i ett beslut

Använd ”Vad är en progressive web app?” i ett konkret beslut. Vi kan knyta begreppet till systemet du bygger eller köper. Gränssnitt som laddar snabbt, fungerar i mobilen och beter sig likadant i alla webbläsare dina kunder råkar använda.

Se våra tjänster Hör av dig

Granska antagandena bakom ordet

Använd förklaringen och det länkade arbetet för att formulera frågor till projektet. Vi kan undersöka antagandena tillsammans innan ett verktyg väljs. Styrde full stack‑utvecklingen av GIS‑kartor — kartutveckling i full stack, från databas till webbläsare

Styrde full stack‑utvecklingen av GIS‑kartor Hör av dig

Vad det är

En progressive web app, även skriven Progressive Web App och oftast förkortad PWA, är en webbplats som är byggd så att en webbläsare kan installera den och köra den som en app: från en egen ikon, i ett eget fönster och ofta så att en del av den fortfarande fungerar när nätet är borta. Den förblir en vanlig webbplats hela tiden. Ingenting kommer från en appbutik, och sidorna är samma HTML, CSS och JavaScript som en webbläsare annars skulle läsa in i en flik.

Ingen standard definierar begreppet. Det standarderna definierar är de två delar som det byggs av. Den första är ett webbappsmanifest, en JSON-fil som beskrivs i W3C-specifikationen Web Application Manifest och som ger appen ett namn, ikoner, en start-URL och ett visningsläge. Specifikationen lägger ribban lågt för vem som räknas: ”Any website is an installable web application.” En webbläsare kan erbjuda ett sätt att installera en sådan webbplats, och den installerade appen öppnas sedan med inställningarna från manifestet. I visningsläget standalone ska den likna en nativ applikation, vilket kan innebära ett eget fönster och en ikon i programstartaren, och webbläsaren utelämnar sitt vanliga gränssnitt, till exempel adressfältet.

Den andra delen är en service worker från W3C-specifikationen Service Workers. Det är ett skript som webbläsaren kör skilt från sidan, som står mellan sidan och nätverket, ser de förfrågningar som sidan gör och kan besvara dem från cachar som skriptet själv styr. Det är det som gör att en webbapp kan fortsätta leverera sina sidor offline. De cacharna tillhör skriptet: specifikationen håller dem helt åtskilda från webbläsarens vanliga HTTP-cache, och de ändras bara när skriptet ändrar dem.

I Web Application Manifest förekommer inte ordet ”progressive” alls, och specifikationen Service Workers använder det en gång, i en anmärkning om hur Chromium känner igen en PWA. Guiden från Next.js om PWA:er rekommenderar progressiv förbättring (progressive enhancement), så att appen fortfarande fungerar bra i en webbläsare som saknar stöd för vissa av PWA-funktionerna. I den meningen är varje del valfri. En webbläsare som ignorerar manifestet, eller som saknar stöd för service workers, visar ändå webbplatsen.

En progressive web app är inte heller en egen sorts webbplats. En ensidesapp, en statisk webbplats och en webbplats med server-side rendering kan alla lägga till ett manifest och en service worker. Begreppet beskriver vad en webbläsare kan göra med en webbplats när den väl har lästs in, inte hur dess HTML tas fram.

Varför det spelar roll

Guiden från Next.js lägger fram tre argument. Uppdateringar går ut direkt, utan att vänta på godkännande i en appbutik. En kodbas räcker för alla plattformar. Och appen får funktioner som folk förväntar sig av nativa appar, till exempel installation på hemskärmen och pushnotiser. Alla tre följer av att appen fortfarande är en webbplats: en ny version finns på varje enhet så snart den har driftsatts.

Offline är skälet till att service workern finns. Specifikationens eget avsnitt om motivationen säger att webbapplikationer traditionellt har utgått från att nätverket går att nå, och att det försätter webbinnehåll i ett sämre läge än andra teknikstackar. Service workern är utformad för att rätta till den obalansen. När en sådan finns på plats behöver ett tåg i en tunnel eller ett fartyg långt från kusten inte längre sluta på webbläsarens felsida.

De två halvorna är oberoende av varandra. Guiden från Next.js påpekar att ett erbjudande om installation inte kräver offlinestöd, och manifestspecifikationen har tagit bort sin egen medlem serviceworker. En app kan vara installerbar utan service worker, och en service worker kan få en vanlig webbplats att fungera offline helt utan manifest.

Det båda halvorna har gemensamt är HTTPS. Next.js nämner en webbplats som levereras över HTTPS som en av två saker som en app behöver innan den kan installeras på hemskärmen i en mobil. Den andra är ett giltigt manifest. Specifikationen Service Workers säger att service workers måste köras i säkra kontexter, vilket i praktiken betyder HTTPS, och att localhost är tillåtet under utveckling.

Så ser det ut i praktiken

Tre små filer räcker till en fungerande PWA, och alla är skrivna för den här artikeln. Manifestet ger appen ett namn, väljer visningsläge och listar ikonerna:

{
  "name": "Field Notes",
  "short_name": "Notes",
  "start_url": "/",
  "display": "standalone",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

Varje sida länkar till manifestet och registrerar service workern, men bara i en webbläsare som stöder det:

<link rel="manifest" href="/manifest.webmanifest" />
<script>
  if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("/sw.js");
  }
</script>

Och själva service workern sparar en liten uppsättning filer för offlinebruk:

// sw.js
const CACHE = "shell-v2";
const SHELL = ["/offline.html", "/app.css", "/app.js"];

async function precache() {
  const cache = await caches.open(CACHE);
  await cache.addAll(SHELL);
}

async function dropOldCaches() {
  const names = await caches.keys();
  const old = names.filter((name) => name !== CACHE);
  await Promise.all(old.map((name) => caches.delete(name)));
}

async function respond(request) {
  if (request.mode === "navigate") {
    try {
      return await fetch(request);
    } catch {
      return caches.match("/offline.html");
    }
  }
  const hit = await caches.match(request);
  return hit ?? fetch(request);
}

self.addEventListener("install", (event) => {
  event.waitUntil(precache());
});

self.addEventListener("activate", (event) => {
  event.waitUntil(dropOldCaches());
});

self.addEventListener("fetch", (event) => {
  if (event.request.method !== "GET") return;
  event.respondWith(respond(event.request));
});

Manifestet använder .webmanifest, den filändelse som är registrerad för det, och levereras med medietypen application/manifest+json. Service workern ligger i roten eftersom dess omfång (scope) som standard är katalogen som skriptet ligger i, så en worker på /js/sw.js skulle bara kunna styra sidor under /js/ om inte servern skickade ett Service-Worker-Allowed-huvud. När den installeras öppnar den en cache som heter shell-v2 och sparar reservsidan för offlinebruk, stilmallen och skriptet där. addAll() avvisar om någon av filerna kommer tillbaka med en felstatus, och eftersom waitUntil() fick det promiset misslyckas hela installationen tillsammans med det, i stället för att lämna en halvfylld cache efter sig. Vid aktiveringen raderar den varje cache med ett annat namn. Sidnavigeringar går först till nätverket och faller tillbaka på den sparade reservsidan när nätverket inte svarar. Allt annat levereras från cachen när det finns där och hämtas när det inte gör det. Förfrågningar som inte är GET lämnar den i fred.

Företaget beskriver ett fullständigt bygge av det här på en riktig plattform. Portfoliosidan om PWA-arbetet förklarar varför det behövdes: många av plattformens användare är sjöfolk och fältpersonal som använder mobilen, ofta offline eller på en svag uppkoppling, så appen måste kunna installeras som en nativ app och fortsätta fungera när signalen tappas. Den täcker båda halvorna. Appen installeras på hemskärmen med ikoner i flera storlekar, en startskärm och temafärger. Service workern kommer på plats med hjälp av next-pwa, och cachningen i Workbox under körning levererar typsnitt, bilder, ljud, video och CDN-resurser enligt CacheFirst, eftersom inget av det ändras mellan förfrågningar, tillsammans med HTTP-huvuden för cache-control. Eftersom service workers kräver HTTPS testades installationen och offlinebeteendet över HTTPS på riktiga enheter. Resultatet som sidan redovisar är en app som folk installerar och fortsätter att använda offline, där upprepade besök hämtas ur cachen.

Installationshalvan ser också annorlunda ut från läsarens sida. Webbplatsens sida om att installera den som en app visar var varje webbläsare har kommandot: knappen Dela och Lägg till på hemskärmen i Safari på en iPhone, en liten ikon ytterst till höger i adressfältet i Chrome, Edge, Brave, Opera och Vivaldi på en dator, och Lägg till i Dock i Safari på macOS Sonoma eller senare. Den säger också att iOS aldrig själv föreslår installation och att Firefox på datorn saknar ett sätt att installera webbappar. Och den är tydlig med vad installationen ger: en ikon och ett fönster, men ingen kopia för offlinebruk, eftersom en webbplats bara fungerar offline när den har en service worker.

Var det går fel

Användarna fastnar i den gamla versionen. Enligt specifikationen Service Workers uppdateras cacheobjekt bara när författaren uttryckligen ber om det, och de förfaller aldrig av sig själva utan finns kvar tills författaren raderar dem. En worker som går till cachen först levererar därför gårdagens stilmall tills skriptet säger något annat. Sätt en version i cachens namn, ändra den varje gång de cachade filerna ändras och radera de gamla cacharna vid aktiveringen, som exemplet gör. Det är en ändring i skriptet som startar en uppdatering, eftersom webbläsaren jämför den nyss hämtade sw.js byte för byte med den installerade. Även då väntar den nya workern tills ingen flik eller inget fönster längre använder den gamla, om den inte anropar skipWaiting(), så en användare som har en flik öppen kör den tidigare versionen så länge fliken är öppen. Guiden från Next.js levererar sw.js med Cache-Control: no-cache, no-store, must-revalidate, så att användarna alltid får den senaste versionen av själva workern.

Det fungerar på den bärbara datorn men inte på testtelefonen. navigator.serviceWorker finns bara i säkra kontexter. På en sida som levereras över ren HTTP blir den vanliga funktionskontrollen helt enkelt false, workern registreras aldrig, och sidan säger ingenting om det. Specifikationen låter webbläsare godta localhost, 127.0.0.0/8 och ::1/128 under utveckling, och därför kan en worker som körs på utvecklarens egen dator saknas när en telefon öppnar samma app över HTTP från datorns adress i det lokala nätverket. Next.js erbjuder next dev --experimental-https för lokal testning över HTTPS. Testa installation och offlinebeteende på riktiga enheter, över HTTPS, innan du kallar något av dem klart.

En fetch-lyssnare som inte gör något. Specifikationen Service Workers noterar att vissa webbplatser registrerar en fetch-lyssnare med tom kropp bara för att Chromium ska känna igen dem som en progressiv webbapplikation. Specifikationen upptäcker nu lyssnare som () => {} och uppmuntrar webbläsare att varna för att sådana lyssnare är onödiga och kan försämra prestandan. Skriv en hanterare som faktiskt besvarar förfrågningar, eller låt bli att lägga till lyssnaren.

Att räkna med att webbläsaren föreslår installation. Om installation ska erbjudas, och hur, avgör webbläsaren. Next.js skriver att moderna webbläsare automatiskt erbjuder installation när en webbplats har ett giltigt manifest och HTTPS, och avråder från att bygga en egen installationsknapp på beforeinstallprompt, eftersom den händelsen inte fungerar i alla webbläsare och på alla plattformar, bland annat inte i Safari på iOS. Manifestspecifikationen har sedan dess tagit bort händelsen helt. Visningsläget och start-URL:en är också bara rekommendationer: en webbläsare som inte stöder standalone faller tillbaka på minimal-ui och sedan på browser, en vanlig flik. Förklara för användarna hur de installerar, som instruktionerna för iOS i guiden från Next.js gör, och se till att appen fungerar även när den öppnas i en flik.

Läs mer

Ordlista

Välj utifrån dina faktiska förutsättningar

Ta med arbetsflödet, befintliga gränssnitt och användarna som behöver förändringen. Vi kan definiera en testbar leverans och en överlämning teamet kan förvalta.

Se vad det kostar Hör av dig

Gör begreppen praktiska

Fråga hur ”Vad är en progressive web app?” gäller i din organisation. Ett konkret exempel från ditt nuvarande system räcker för att börja samtalet.

Se våra tjänster
welcome@engineer.company +45 50 10 61 84 Köpenhamn, Danmark
QR-kod för Progressive web app
Ta med dig den här sidanhttps://engineer.company/sv/glossary/progressive-web-app/