Offline stránka v PWA přes ServiceWorker

PWA (Progressive Web Apps) je postup, jak vytvářet webové stránky, které se chováním podobají nativním aplikací.

Tedy v tomto případě například fungují bez připojení k internetu.

Proč mít PWA

V praxi je to dobré například k tomu, aby stránku šlo v Chrome nainstalovat.

Instalace aplikace

Výsledkem je potom v macOS upravené okno prohlížeče Chrome a přidání ikony mezi ostatní aplikace:

Nainstalovana aplikace

Na Androidu to jde takový web připnout na plochu mezi ostatní aplikace.

Chrome jeden čas upozorňoval, že v budoucnu bude možná vyžadovat i offline fungování.

Site cannot be installed: Page does not work offline. Starting in Chrome 93, the installability criteria is changing, and this site will not be installable. See https://goo.gle/improved-pwa-offline-detection for more information.

Nakonec byl tento požadavek pro splnění PWA odložen. Ale kdo ví, kdy zase přijde.

Ověřit, co má stránka splňovat, aby byla validní PWA, jde zjistit v DevTools na záložce Lighthouse:

Validace PWA v Chrome

Vytvoření ServiceWorkeru

Aby vůbec mohl ServiceWorker fungovat, musí web běžet na HTTPS (výjimkou je vývoj na localhostu).

ServiceWorker je skript psaný v JavaScriptu. Většinou se tento soubor jmenuje sw.js nebo serviceWorker.js.

Záleží na tom, kde je umístěn. Je to dost důležité, protože pokud bude registrován z umístění např. assets/js/sw.js, nebude fungovat na adresách, které nezačínají assets/js.

Proto se většinou dává rovnou do rootu webu. Případně jde nastavit rozsah působení parametrem scope:

if('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/serviceWorker.js', { scope: '/'})
}

Výše uvedený kód zaregistruje SW, pokud ho prohlížeč podporuje.

Offline stránka offline.html

Offline stránka může být pro jednoduchost obyčejný HTML soubor s inline styly i obrázky:

<!doctype html>
<meta charset="utf-8">
<title>Offline</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
Jste offline.

V offline stránce je možné používat JS, takže není problém například přidat chování, aby se stránka sama obnovila, když je uživatel opět online:

<script>
    window.addEventListener('online',  function () {
        window.location.reload()
    })
</script>

Jde používat i externí styly, skripty nebo obrázky, ale musely by se rovněž přidat do cache.

Obsah serviceWorker.js

Úplně základní offline stránku stačí při instalaci workeru uložit do cache.

Třeba následovně:

const CACHE_VERSION = 1
const CACHE = {
    OFFLINE: 'offline-cache-' + CACHE_VERSION,
}
const OFFLINE_URL = 'offline.html'

self.addEventListener('install', function (event) {
    event.waitUntil(
        caches.open(CACHE.OFFLINE).then(function (cache) {
            return cache.add(OFFLINE_URL)
        })
    )
})

Tento kód při instalaci SW (událost install) otevře cache s názvem offline-cache-1 (případně ji vytvoří, pokud neexistuje – těchto keší může být i víc) a přidá do ní obsah z URL offline.html.

Druhý úkol je odchytávat fetch události. Tam si je možné upravit chování standardních požadavků na soubory.

A v případě chyby vrátit offline.html z cache:

self.addEventListener('fetch', function (event) {
    if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
        event.respondWith(
            (async function () {
                try {
                    return await fetch(event.request, { redirect: 'manual' })
                } catch {
                    const cache = await caches.open(CACHE.OFFLINE)
                    return await cache.match(OFFLINE_URL)
                }
            })()
        )
    }
})

To je celé.

Celý kód je obalen do podmínky omezující vykonávání na případy, kdy je cílem získat dokument. Konstrukce event.request.mode nefunguje všude, proto je uvedena ještě alternativní podmínka. Jsou tam kvůli tomu, aby se v případě požadavků třeba na obrázky nic nedělalo.

Za povšimnutí stojí { redirect: 'manual' } – bez toho nemusí offline stránka fungovat, pokud se na doméně používá nějaké přesměrování.

Volání standardního požadavku metodou fetch je v trycatch bloku, takže v případě chyby (tj. uživatel je offline) se otevře cache a pokusí se v ní najít soubor offline.html.

Na kartě Network je potom u requestů vidět, že je zpracoval ServiceWorker (znázorňuje to ozubené kolečko před názvem):

Zpracování požadavku SW

Debugování a vývoj

Základem je karta Application ve vývojářských nástrojích Chrome.

Je zde vidět samotný Service Worker – v jakém je stavu:

Pro vývoj se hodí možnost Update on reload a pro otestování si zapnout Offline. Jen pozor na to, že obě zaškrtnuté položky způsobí, že se offline stránka nezobrazí.

Dev Tools ServiceWorker

Užitečné může být i podívat se do cache:

Dev Tools cache

Je dobré tohle fakt dobře otestovat, protože nakešovaný nefunkční serviceWorker.js může klidně návštěvníkům web úplně vyřadit.

Když ServiceWorker zpracovává všechny požadavky na HTML stránky jako na příkladu výše, může kompletně rozbít toto servírování obsahu návštěvníkovi.

Soubor manifest.json

Další podmínkou PWA je manifest.json – jedná se o soubor ve formátu JSON, kde jsou základní informace o aplikaci a odkazy na ikony.

Asi nejsnazší je si takový soubor vytvořit použitím nějakého generátoru:

Výstupem je něco takového:

{
  "name": "Je čas",
  "short_name": "Je čas",
  "theme_color": "#1079CF",
  "background_color": "#fff",
  "display": "standalone",
  "orientation": "portrait",
  "scope": "/",
  "start_url": "/",
  "icons": [
    {
      "src": "images/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
  ]
}

Tento soubor se připojuje značkou <link> v hlavičce webu.

Maskable ikona

Posledním trendem v ikonách aplikací je tzv. maskable formát. Řeší to problém, kdy různé verse a nadstavby Androidu mají jinak přizpůsobené ikony – zejména jejich zaoblení.

Zaoblení maskable ikony

Aby se nemusely vytvářet různé varianty, maskable formát definuje ochrannou zónu kolem ikony.

Ochranná zóna ikony

To znamená, že cokoliv mimo zelený kruh může systém libovolně zprůhlednit.

Pro připravení vlastní maskable ikony se hodí:

Odkaz na tuto ikonu je následně potřeba přidat do manifest.json mezi ostatní ikony (do pole icons):

    {
      "src": "images/icons/maskable_icon.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable",
      "density": 4
    }

Má smysl mít PWA?

Při splnění kroků výše bude web splňovat formální požadavky na PWA, které je možné automaticky testovat Lighthouse nástrojem ve vývojářských nástrojích.

Validní PWA Lighthouse

Podobně jako kdysi řešená validita HTML/CSS kódu nezajišťovala automaticky dobrý web, ani stránka splňující PWA nemusí být nikterak lepší.

Příklad popsaný v tomto článku je tak trochu PWA pro PWA – praktický přínos pro uživatele je dost malý.

Výhoda PWA je v podstatě jen možnost instalace na plochu na Androidu, kde je otázka, jestli to významné množství lidí potřebuje.

Význam takto hloupé offline.html stránky je trochu sporný.

Standardní hláška prohlížeče má výhody v tom, že je na ní návštěvník zvyklý, je přeložená do jeho jazyku a případně respektuje i nastavení světlého/tmavého režimu:

Chrome offline stránka

Vlastní offline stránka tak může být pro návštěvníky zhoršení situace.

Nevýhoda PWA spočívá v tom, že se musí navíc:

  1. Spustit JS kód registrující ServiceWorker.
  2. Stáhnout a vykonat/instalovat kód v souboru serviceWorker.js.
  3. Stáhnout a uložit do cache obsah stránky offline.html.
  4. Stáhnout a zpracovat obsah souboru manifest.json.
  5. Každý požadavek na soubory musí projít logikou ServiceWorkeru.

První příchod na stránku tak znamená stahovat další 3 soubory navíc. K tomu sice malou, ale pořád nějakou zátěž v podobě JavaScriptu pro registraci, instalaci a samotného běhu SW.

Vytvářet PWA tak dává smysl spíš v situacích, kdy to nabídne návštěvníkům více funkcionality – například push notifikace.

Případně když stránka nabídne opravdové offline použití, ne jen vlastní chybovou offline stránku.

To je dobře proveditelné u SPA (single page aplikací), kde se může díky ServiceWorkeru uložit do cache celá logika aplikace včetně skriptů, stylů nebo obrázků a opakovaný příchod na takovou stránku je téměř okamžitý, protože spuštění celé aplikace obslouží SW z cache.

Risiko rozbití webu

ServiceWorker s offline fungováním přináší dost velkou šanci poměrně zásadně rozbít celý web.

Je to tím, že pro offline fungování stránky veškeré požadavky prochází přes ServiceWorker. Takže když tam bude chyba, nemusí vůbec nic fungovat.

Velmi vhodné je mít před spuštěním na webu monitoring typu Sentry.

U větších webů ideálně pustit úpravu nejprve na menší část návštěvníků.

Odkazy jinam

To je všechno. Líbil se vám článek a chcete se dozvědět, až vyjde další?

Sledujte:

 

Připomínky mi pište do komentářů ↓

Offline webová stránka

Offline webová stránka

Jak umožnit návštěvníkům stažení celé webové stránky pro prohlížení offline, umístění na CD apod.

Je uživatel online?

Je uživatel online?

Jak zjistit, jestli je uživatel webové aplikace online nebo offline.

Fixní postranní panel

Jak vytvořit fixovaný banner?

JavaScriptové řešení prvku, který při rolování zůstane stále viditelný.

Aktivování/deaktivování okna

Zachycení aktivování a deaktivování okna v JS

V JavaScriptu lze relativně snadno reagovat na aktivování nebo deaktivování okna/záložky/tabu.

Baterka v CSS a JavaScriptu

Vytvoření efektu baterky v CSS a JS

Jak jednoduše vytvořit na stránce efekt baterky? Tedy ztmavit web a prohlížet ho jakýmsi průzorem.

Komentáře