HTML5 offline webové aplikace

HTML5 Badge

Úvod do tvorby offline webových aplikací z populární knížky Marka Pilgrima Dive into HTML5. Naučíte se psát základní manifest a předvedeme vám offline verzi hry halma.

Tento článek je překladem kapitoly Let’s Take This Offline knihy Marka Pilgrima Dive into HTML5, jejíž komunitní překlad probíhá na HTML5.cz a je šířena pod licencí CC-BY-3.0.

Co je offline webová aplikace? Na první pohled to zní jako protimluv. Webové stránky si stáhnete a zobrazíte. Stahování znamená dostupné připojení k síti. Jak můžete stahovat, když jste offline? Samozřejmě, že nemůžete. Ale můžete stahovat, když jste online. A přesně tak offline aplikace v HTML5 fungují.

Offline webová aplikace je jen seznam URL — HTML, CSS, JavaScriptu, obrázků a jakýchkoliv dalších souborů. Hlavní stránka offline webové aplikace na tento seznam (tzv. soubor manifestu) odkazuje. Je to vlastně obyčejný textový soubor umístěný na webovém serveru. Webový prohlížeč, který podporuje offline aplikace v HTML5 si stáhne seznam URL ze souboru manifestu, stáhne samotné soubory, uloží je lokálně a udržuje je aktuální. Když poté zkusíte otevřít webovou aplikaci bez internetového připojení, webový prohlížeč automaticky zobrazí tyto lokálně uložené soubory.

Zbytek práce je na vás, na webovém vývojáři. V DOMu je vlastnost, která vám řekne, zda jste online nebo offline. Existují události, které se zavolají při změně stavu připojení (chvíli jste offline a za minutu zase online, atd.). Ale to je tak všechno. Pokud vaše aplikace vytváří data nebo ukládá svůj stav, je na vás je uložit lokálně, když jste offline a pak je synchronizovat se serverem, když jste zase online. Jinak řečeno, HTML5 vám sice umožní mít webovou aplikaci offline, ale co ta aplikace pak bude dělat, je na vás.

Podpora offline režimu
IE Firefox Safari Chrome Opera iPhone Android
· 3.5+ 4.0+ 5.0+ 10.6+ 2.1+ 2.0+

Cache Manifest

Offline webová aplikace se točí kolem souboru cache manifestu. Co je soubor manifestu? Je to seznam všech souborů, které by webová aplikace mohla potřebovat při práci v offline režimu. Stahování a ukládání těchto souborů zajistíte přidáním atributu manifest do elementu  <html>.

<!DOCTYPE HTML>
<html manifest="/cache.manifest">
<body>
...
</body>
</html>

Soubor cache manifestu může být umístěn kdekoliv na webovém serveru, ale musí být posílán s content type text/cache-manifest. Pokud používáte webový server Apache, mělo by stačit přidat příkaz AddType do souboru .htaccess v kořenovém adresáři vašeho webu:

AddType text/cache-manifest .manifest 

Potom ověřte, že jméno souboru cache manifestu končí na .manifest. Pokud používáte jiný webový server nebo jinak nastavený Apache, podívejte se do dokumentace serveru, jak se hlavička Content-Type nastavuje.

Q: Moje webová aplikace obsahuje více stránek. Musím mít atribut manifest v každé z nich nebo ho stačí dát na hlavní stránku?

A: Každá stránka vaší webové aplikace musí mít atribut manifest, který odkazuje na cache manifest pro celou aplikaci.

Dobře, takže každá HTML stránka odkazuje na soubor cache manifestu a ten je posílán se správnou Content-Type hlavičkou. Ale co patří do souboru manifestu? Tady to teprve začíná být zajímavé.

První řádek každého cache manifestu je:

CACHE MANIFEST 

Dále jsou všechny soubory manifestu rozděleny do tří částí: sekce “explicit”, sekce “fallback”, a sekce “online whitelist”. Každá sekce má svoji hlavičku na samostatném řádku. Pokud soubor manifestu neobsahuje žádné nadpisy sekcí, jsou všechny uvedené položky automaticky zařazeny do sekce “explicit”. Nezdržujme se teorií, mohla by vám z ní vybouchnout hlava.

Takto vypadá validní soubor manifestu. Obsahuje tři položky: CSS soubor, soubor JavaScriptu a JPEG obrázek.

CACHE MANIFEST
/clock.css
/clock.js
/clock-face.jpg 

Tento soubor manifestu nemá žádné nadpisy sekcí, takže všechny uvedené položky jsou automaticky v sekci “explicit”. Tyto položky se automaticky stáhnou, uloží lokálně a v případě, kdy nebude dostupné připojení k síti, se použijí se místo svých online protějšků. Takže po načtení tohoto souboru manifestu si prohlížeč z kořenového adresáře webového serveru stáhne clock.css, clock.js a clock-face.jpg. Poté můžete odpojit síťový kabel, obnovit stránku a všechny tyto soubory budou dostupné offline.

Q: Musím v cache manifestu uvádět i HTML stránky?
A: Ano i ne. Pokud je celá vaše webová aplikace obsažena v jedné stránce, ujistěte se jen, že ta stránka ukazuje pomocí atributu manifest na cache manifest. Pokud otevřete HTML stránku s atributem manifest, předpokládá se, že samotná stránka je součástí webové aplikace, takže není nutné ji uvádět do souboru manifestu. Nicméně, pokud se vaše webová aplikace skládá z více stránek, měli by jste je zapsat do souboru manifestu, jinak prohlížeč nebude vědět, že má stáhnout a uložit i další HTML stránky.

Sekce Network

Teď tu máme trochu složitější příklad. Předpokládejme, že chcete sledovat návštěvníky vaší hodinové aplikace pomocí skriptu tracking.cgi, který se volá z atributu <img src>. Cachování tohoto souboru by zmařilo sledování, takže tento soubor by se neměl cachovat ani být k dispozici offline. Lze to vyřešit takto:

CACHE MANIFEST
NETWORK:
/tracking.cgi
CACHE:
/clock.css
/clock.js
/clock-face.jpg 

Tento soubor cache manifestu obsahuje nadpisy sekcí. Řádek označený NETWORK: je začátek sekce “online whitelist”. Položky uvedené v této sekci nejsou nikdy cachovány a nebudou k dispozici offline. (Při pokusu je načíst v offline režimu dostaneme chybovou hlášku.) Řádek nadepsaný CACHE: je začátek sekce “explicit”. Zbytek souboru cache manifestu je stejný jako v předchozím příkladu. Všechny uvedené soubory budou cachovány a k dispozici i offline.

Záložní (fallback) sekce

V souboru cache manifestu existuje ještě jeden typ sekce: záložní sekce. V této sekci můžete určit náhrady pro online zdroje, které nemohou být z jakéhokoliv důvodu cachovány nebo se je nacachovat nepodařilo. Ve specifikaci HTML5 najdeme pěkný příklad použití fallback sekce:

CACHE MANIFEST
FALLBACK:
/ /offline.html
NETWORK:
* 

A co to dělá? Představme si web, který obsahuje miliony stránek, jako je třeba Wikipedie. Není možné stáhnout celý web, a to ani nechceme. Ale předpokládejme, že je možné zpřístupnit část webu offline. Ale jak se rozhodneme, které stránky budeme cachovat? Co třeba takhle: každá stránka, kterou na této hypotetické offline Wikipedii navštívíme bude stažena a cachována. To by zahrnovalo každý encyklopedický článek, který jste kdy navštívili, každou diskuzi (kde můžete diskutovat o konkrétním encyklopedickém článku), a každou editační stránku, kde můžete ten encyklopedický článek upravovat.

To je to, co dělá soubor cache manifestu. Předpokládejme, že každá HTML stránka (článek, diskuze, editace, historie) na Wikipedii ukazuje na tento soubor cache manifestu. Když jakoukoliv takovou stránku navštívíte, prohlížeč si řekne “hej, tahle stránka je část offline webové aplikace, znám už tuhle konkrétní?” Pokud prohlížeč zatím nikdy tento soubor cache manifestu nestáhnul, vytvoří novou appcache (zkratka pro “aplikační cache”), stáhne všechny zdroje uvedené v cache manifestu, a nakonec přidá aktuální stránku do appcache. Pokud prohlížeč už o tomto cache manifestu ví, tak jen přidá aktuální stránku do již existující appcache. Ať už tak nebo tak, stránka, kterou jste navštívili, skončí v appcache. Tohle je důležité. Znamená to, že máte offline webovou aplikaci, která si “postupně” přidává stránky, když je navštívíte. Nemusíte tedy vypisovat každou HTML stránku do cache manifestu.

Teď se podíváme na fallback sekci. Fallback sekce v tomto cache manifestu má jen jeden řádek. První část řádku (před mezerou) není URL. Ve skutečnosti to je šablona URL. Znak ( /) odpovídá jakékoliv stránce na webu, ne jen hlavní stránce. Pokud zkusíte navštívit stránku, když jste offline, prohlížeč ji zkusí načíst z appcache. Pokud ji tam najde (protože jste ji navštívili, když jste byli online a byla tedy implicitně přidána do appcache), tak zobrazí nacachovanou kopii. Pokud prohlížeč stránku v appcache nenajde, místo vypsání chybové hlášky, zobrazí stránku /offline.html, jak je určeno v druhé polovině řádku.

Nakonec se podíváme na network sekci. Network sekce má v tomto cache manifestu také jen jeden řádek, který obsahuje jen jeden znak ( *). Tento znak má v network sekci speciální význam. Říká se mu “zástupný znak”. To je elegantní způsob jak říct, že cokoliv co není v appcache, se má stáhnout z originální webové adresy, pokud je dostupné připojení k internetu. To je důležité pro “neuzavřenou” offline webovou aplikaci. To znamená, že pokud procházíte tuto hypotetickou offline-aktivní Wikipedii online, prohlížeč bude standardně stahovat obrázky, videa a další vložené položky, i pokud jsou na jiné doméně. (To je běžné na velkých webech, i pokud nejsou součástí webové aplikace. HTML stránky jsou generovány a načítány lokálně a obrázky a videa jsou distribuovány z CDN na jiné doméně.) Bez tohoto zástupného znaku by se naše offline-aktivní Wikipedie chovala divně, pokud byste byli online — konkrétně by nenačítala jakékoliv externě uložené obrázky nebo videa!

Je tento příklad kompletní? Ne. Wikipedie je víc než HTML soubory. Na každé stránce používá společné CSS, JavaScript a obrázky. Každý z těchto zdrojů by musel být explicitně uvedený v sekci CACHE: v manifest souboru, aby se stránky správně zobrazovaly a chovaly i offline. Účelem fallback sekce je to, že můžete mít “neuzavřenou” offline webovou aplikaci, která obsahuje i soubory, které nejsou explicitně uvedeny v souboru manifestu.

Průběh událostí

Zatím jsem o offline webových aplikacích, cache manifestu a offline aplikačním cache (“appcache”) mluvil vágně, skoro magicky. Věci se stahují, prohlížeče rozhodují a všechno Prostě Funguje. Ale vy víte svoje, že? Vždyť mluvíme o tvorbě webů. Nikdy se nestane, aby to Prostě Fungovalo.

Nejdříve si pojďme popsat průběh událostí, zvláště DOM událostí. Když prohlížeč načte stránku, která obsahuje cache manifest, zavolá na objektu window.applicationCache řadu událostí. Vím, že to vypadá komplikovaně, ale věřte mi, že je to nejjednodušší způsob, jak jsem to mohl popsat, bez toho abych vynechal důležité informace.

  1. Hned jak prohlížeč zjistí existence atributu manifest v elementu <html>, zavolá událost checking . (Všechny zde popsané události jsou volané na objektu window.applicationCache). Událost checking je zavolána vždy, bez ohledu na to, jestli už jste předtím navštívili tuto stránku nebo nějakou jinou, která ukazuje na stejný soubor manifestu.
  2. Pokud váš prohlížeč vidí tento soubor cache manifestu poprvé…
    • Zavolá událostdownloading a začne stahovat soubory uvedené v cache manifestu.
    • Během stahování bude pravidelně volat událost progress , která bude obsahovat informace o tom, kolik souborů už bylo staženo a kolik jich ještě zbývá stáhnout.
    • Poté, co se všechny soubory uvedené v cache manifestu stáhnou, prohlížeč zavolá poslední událost, cached . To je signál, že offline webová aplikace je celá nacachovaná a připravená k použití offline. Tak a je hotovo.
  3. Na druhou stranu, pokud už jste předtím navštívili tuto nebo jinou stránku, která odkazuje na stejný soubor cache manifestu, prohlížeč už o něm ví a může už mít některé soubory uložené v appcache. Dokonce může mít v appcache uloženou celou offline webovou aplikaci. Otázkou je, zda se soubor cache manifestu změnil od posledního načtení prohlížečem.
    • Pokud se nezměnil, prohlížeč ihned zavolá událost noupdate . A hotovo.
    • Pokud je odpověď ano, cache manifest se změnil, tak prohlížeč zavolá událostdownloading a začne stahovat všechny soubory uvedené v cache manifestu.
    • Během stahování prohlížeč průběžně volá událost progress , která obsahuje informaci o tom, kolik souborů už bylo staženo a kolik jich je ještě ve frontě ke stažení.
    • Poté, co se všechny soubory uvedené v cache manifestu úspěšně znovustáhnou, prohlížeč zavolá poslední událost, updateready . To je signál, že nová verze vaší webové aplikace je zcela nacachovaná a připravená k použití offline. Nová verze se nicméně ještě nepoužívá. Abychom ji zapnuli bez nutnosti obnovit stránku, můžete ručně zavolat funkci window.applicationCache.swapCache().

Pokud během procesu dojte k jakékoliv chybě, prohlížeč zavolá událosterror a zastaví. Následuje beznadějně nekompletní seznam toho, co se mohlo pokazit:

  • Cache manifest vrátil HTTP error 404 (Page Not Found) nebo 410 (Permanently Gone).
  • Cache manifest byl nalezen nezměněný, ale HTML stránku, která na něj odkazuje, se nepodařilo stáhnout.
  • Cache manifest se změnil v průběhu aktualizace.
  • Cache manifest byl změněn a správně stažen, ale prohlížeči se nepodařilo stáhnout některý ze v něm uvedených souborů.

Umění ladění, a.k.a. “Zabijte mě! Zabijte mě teď!”

Chtěl bych zdůraznit dvě důležité věci. První je něco, co jste si právě přečetli, ale věřím, že je nutné to zopakovat znovu: když se nepodaří načíst byť i jediný soubor uvedený v cache manifestu, celý proces ukládání offline aplikace selže. Prohlížeč zavolá událost error, ale nebude na první pohled jasné, co je za problém. Kvůli tomu může být ladění offline webových aplikací více frustrující než obvykle.

Druhá věc je něco, co technicky není chyba, ale dokud nepochopíte, jak to funguje, tak to bude vypadat jako bug v prohlížeči. Souvisí to s tím, jak přesně prohlížeč pozná, jestli se soubor cache manifestu změnil. Je to třífázový proces. Je to nudné, ale důležité, tak dávejte pozor.

  1. Podle běžné sémantiky HTTP prohlížeč pozná, jestli vypršela platnost souboru cache manifestu. Webový server k němu, stejně jako k jakémukoliv jinému souboru přístupnému přes HTTP, přidá do HTTP hlaviček odpovědi další meta-informace. Některé z těchto HTTP hlaviček ( Expires a Cache-Control) říkají prohlížeči, jak má dovoleno soubory cachovat bez dotazování se serveru na případné změny. Tento způsob cachování nemá nic společného s offline webovými aplikacemi. Funguje to úplně stejně s jakoukoliv HTML stránkou, stylem, skriptem, obrázkem nebo čímkoliv dalším na webu.
  2. Pokud vypršela platnost cache manifestu (podle jeho HTTP hlaviček), tak se prohlížeč zeptá serveru, zda existuje novější verze a pokud ano, tak ji prohlížeč stáhne. Prohlížeč to udělá tak, že pošle HTTP požadavek, který bude obsahovat datum a čas poslední změny cache manifestu, kterou webový server poslal ve hlavičkách HTTP odpovědi při minulém stažení. Pokud webový server zjistí, že se cache manifest od toho data nezměnil, tak jen vrátí status 304 (Not Modified). Tohle opět není specifické pro offline webové aplikace, ale funguje to tak prakticky se všemi soubory na webu.
  3. Pokud se podle webového serveru soubor cache manifestu od tohoto data změnil, vrátí HTTP status 200 (OK) následovaný obsahem nového souboru a novými Cache-Control hlavičkami a s novým datem poslední změny, takže kroky 1 a 2 budou příště fungovat správně. (HTTP je cool; webové servery vždy myslí na budoucnost. Pokud už webový server musí poslat soubor, udělá cokoliv, aby zajistil, že ho nebude muset bezdůvodně posílat znovu.) Hned, jak prohlížeč stáhne nový soubor cache manifestu, prohlížeč porovná jeho obsah s tím, který stáhl posledně. Pokud je obsah souboru cache manifestu stejný jako byl minule, prohlížeč znovu nestáhne žádný z uvedených souborů.

Kterýkoliv z těchto kroků vám může způsobit nepříjemnosti při vývoji a testování vaší offline webové aplikace. Například řekněme, že nasadíte jednu verzi cache manifestu, a o 10 minut později zjistíte, že do něj potřebujete přidat další soubor. Žádný problém, že? Jen přidáme další řádek a znovu nasadíme. Aj. Dojde k tomuto: obnovíte stránku, prohlížeč zjistí existenci atributu manifest, zavolá událost checking a pak… nic. Prohlížeč tvrdohlavě trvá na to, že soubor cache manifestu se nezměnil. Proč? Protože ve výchozím nastavení, je webový server pravděpodobně nastaven tak, aby prohlížeči říkal (pomocí HTTP sémantiky za použití hlaviček Cache-Control), že statické soubory má cachovat několik hodin. To znamená, že prohlížeč se nikdy v tomto procesu nedostane za krok 1. Webový server samozřejmě ví, že se soubor změnil, ale prohlížeč se nikdy nedostane ani tak daleko, aby se ho zeptal. Proč? Protože když si prohlížeč naposledy stahovat soubor cache manifestu, webový server mu řekl (pomocí HTTP sémantiky za použití hlaviček Cache-Control), aby si ho několik hodin cachoval. A teď, o 10 minut později, přesně to prohlížeč dělá.

Aby bylo jasno, tohle není chyba, ale vlastnost. Všechno funguje přesně tak, jak má. Kdyby webové server neměl možnost, jak říct prohlížečům (a proxy serverům), aby cachovaly, web by se přes noc zhroutil. To pro vás asi není žádná útěcha, po několika hodinách strávenými zkoumáním, proč si prohlížeč nevšiml změněného cache manifestu. (A ještě ke všemu, pokud počkáte dostatečně dlouho, tak to záhadně začne fungovat! Protože vypršela platnost HTTP cache! Přesně tak, jak má! Teď mě můžete zabít!)

Skoro určitě musíte udělat toto: přenastavit webový server tak, aby váš soubor cache manifestu nebyl cachovatelný pomocí HTTP. Pokud máte webový server Apache, stačí na to dva řádky v souboru  .htaccess:

ExpiresActive On
ExpiresDefault "access" 

To skutečně vypne cachování všech souborů v daném adresáři a všech podadresářích. To pravděpodobně není to, co by jste chtěli na produkčním serveru, takže by jste to měli buď omezit pomocí direktivy <Files>, aby to platilo pouze pro soubor cache manifestu a nebo můžete vytvořit podadresář, který bude obsahovat jen soubor .htaccess a cache manifest. Jako obvykle, detaily nastavení jsou pro každý webový server jiné, takže se na řízení cachovacích HTTP hlaviček podívejte do manuálu.

I když vypnete HTTP cachování souboru cache manifestu, pořád budete narážet na to, když změníte některý ze souborů uložených v appcache, který bude stále dostupný na stejné URL na webovém serveru. Tady vám to zkazí 2. krok procesu. Pokud se nezměnil soubor cache manifestu, prohlížeč nezjistí, že se změnil některý z dříve nacachovaných souborů. Podívejte se na následující příklad:

CACHE MANIFEST
# rev 42
clock.js
clock.css 

Pokud změníte clock.css, tak neuvidíte změny, protože samotný soubor cache manifestu se nezměnil. Pokaždé, když změníte některý ze souborů ve vaší offline webové aplikaci, budete muset změnit také soubor cache manifestu. Může to být změna i jen jediného znaku. Nejjednodušší způsob jak toho dosáhnout, je, podle mě, přidání řádku komentáře, který bude obsahovat aktuální číslo revize. Změníte číslo revize v komentáři, webový soubor vrátí nový soubor cache manifestu, prohlížeč zjistí, že se obsah souboru změnil a nastartuje proces znovustažení všech souborů v něm uvedených.

CACHE MANIFEST
# rev 43
clock.js
clock.css 

Pojďme jednu napsat!

Vzpomínáte si na Halmu, kterou jsme si ukázali v kapitole o canvasu a potom vylepšili ukládáním stavu do trvalého lokálního úložiště? Pojďme ji ještě vylepšit a udělat ji offline.

Na to potřebujeme cache manifest, který bude obsahovat vše, co hra potřebuje. Máme hlavní HTML stránku, jeden JavaScriptový soubor, který obsahuje všechen kód hry a… to je vše. Nejsou tam žádné obrázky, protože všechno je vykreslováno programově, přes canvas API. Všechny potřebné CSS styly jsou v elementu <style> v hlavičce HTML stránky. Nás cache manifest vypadá takto:

CACHE MANIFEST
halma.html
../halma-localstorage.js 

Něco málo k cestám. Vytvořil jsem podadresář offline/ v adresáři examples/ a soubor cache manifestu je uložený v něm. Protože HTML stránka potřebuje drobnou úpravu, aby fungovala offline (k tomu se dostaneme později), vytvořil jsem samostatnou kopii HTML souboru, která je také uložena v podadresáři offline/. Protože není potřeba nic měnit v tom JavaScriptovém kódu, kam jsme přidali podporu local storage, využijeme ten stejný soubor, který je v nadřazeném adresáři ( examples/). Soubory tedy vypadají takto:

/examples/localstorage-halma.html
/examples/halma-localstorage.js
/examples/offline/halma.manifest
/examples/offline/halma.html

V souboru cache manifestu ( /examples/offline/halma.manifest) chceme odkázat na dva soubory. Nejprve na offline verzi HTML souboru ( /examples/offline/halma.html). Protože tyto dva soubory jsou v jednom adresáři, je v souboru manifestu uveden bez jakékoliv cesty. Poté, soubor JavaScriptu, který je uložen v nadřazeném adresáři ( /examples/halma-localstorage.js). Ten je v souboru manifestu zapsán pomocí relativní URL: ../halma-localstorage.js. Je to stejné, jako by jste použili relativní URL v atributu <img src>. Jak uvidíte v dalším příkladě, můžete použít i absolutní cesty (začínající v kořeni aktuální domény) a nebo dokonce absolutní URL (odkazující na soubory na jiných doménách).

Teď potřebujeme v HTML souboru přidat atribut manifest, který bude ukazovat na soubor cache manifestu.

<!DOCTYPE html>
<html lang="en" manifest="halma.manifest"> 

A je to! Když prohlížeč s podporou offline režimu poprvé načte offline přístupnou HTML stránku, tak stáhne soubor manifestu a začne stahovat všechny odkazované soubory a ukládat je v appcache. Od této chvíle se při každé návštěvě stránku bude spouštět výše popsaný algoritmus offline aplikace. Můžete hru hrát offline a protože si pamatuje svůj stav, můžete ji kdykoliv zavřít a přijít zpět a pokračovat tam, kde jste skončili.

Další čtení

Standardy:

Dokumentace výrobců prohlížečů:

Tutoriály a dema:

Zbytek právě probíhajícího komunitního překladu knihy Marka Pilgrima Dive into HTML5 najdete na HTML5.cz.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 8

Přehled komentářů

berus97 Super
Unknown Re: Super
Velda Cachování
msx Prečo?
j Re: Prečo?
Martin Hassman Re: Prečo?
Jack Velikost cache
Čelo Re: Velikost cache
Zdroj: https://www.zdrojak.cz/?p=3744