Honza Malý4.1.2018Zpět

Offline v prohlížeči 1. - Detekce, AppCache

Být offline je pro většinu populace stav, který chtějí prožívat co nejméně. Pokud tedy vyloženě nevyhledáváte klid a meditaci pro utřídění myšlenek, ikonka vypadnutého spojení a chybová stránka v prohlížeči není zrovna něco, co by patřilo k světlým momentům každodenní práce.

Offline však zároveň můžete přetvořit ve výhodu, pokud na něj svoji webovou stránku nebo aplikaci připravíte. Představte si, jak se cítí uživatel, když z deseti záložek v prohlížeči jedna z nich - ta vaše - funguje i po ztrátě připojení, v kvalitně připraveném offline režimu. Jak jinak už by si měl uvědomit, že se o jeho pohodlí staráte?

Ovšem pozor, korektní nastavení a správné chování v offline stavu je zároveň v současném světě prohlížečů tak trochu oříšek. Existuje vícero cest, každá z nich má svoje pro a proti, a ne všechny jsou beze zbytku podporovány všemi prohlížeči.

V článku se dozvíme, pro které typy webů nebo aplikací je offline vhodné, jak vůbec detekovat že jsme offline, a popíšeme v současnosti nejvíce se nabízející metodu - Application Cache.

Kdy se offline hodí

Offline se hodí u aplikací typu „do stuff“ - tedy takových, ve kterých odvádíme nějakou reálnou práci. Psaní textů, plánování úkolů, nebo třeba vyplňování dotazníků.

Aplikace typu „do stuff“ lze charakterizovat těmito podmínkami:

  • jejich hlavní náplň práce nevyžaduje být online,
  • jejich stav lze na nějakou dobu pozdržet a uložit později.

Kdy se offline nehodí? Rozhodně tehdy, když aplikace je typu „look up stuff“, tedy webové vyhledávače nebo portály pro získávání informací. Logicky jde o weby velmi výrazně orientované na potřebu být online. Jeden z mála příkladů využití offline by bylo ukládat si výsledky vyhledávání, tedy umožnit vrátit se k nějaké stránce poté, co jsme ztratili připojení. Ovšem pokud tento model dodneška nemá implementovaný ani Wikipedia nebo Google - králové všech „look up stuff“ portálů - poukazuje to na 2 zásadní fakta:

  1. implementačně to není snadné,
  2. rozmyslet si, jak to má vlastně fungovat, taky nebude úplně triviální.

Offline se rozhodně nehodí mít v aplikaci jen proto, že to bezcílně chceme. Pokud si nedokážete scénář reálného využití představit, offline se vůbec nezabývejte.

Jak jsme k tomu došli

Historicky jsme na podporu offline narazili poprvé v požadavku klienta, aby jednostránková JavaScriptová webová aplikace (jednoduchý React + Redux) uměla pracovat i na tabletu, když s ním uživatel zrovna není na síti. V takovém případě by zaznamenala všechny změny a provedla synchronizaci až v okamžiku, kdy bude opět připojena.

Z hlediska dat to zase tak těžké není. Díky Reduxu mám výborný přehled o stavu aplikace a jsem schopen pozdržet jeho uložení do nějakého pozdějšího stavu. Problém by samozřejmě byl, kdybych měl jedna data měněná více než jedním uživatelem - co když je právě online a zapisuje? Pak by se musela praktikovat nějaká synchronizační taktika. Aplikace byla však jednoduchá (jedna session na uživatele) a tento problém jsme zanedbali.

Z hlediska rozmyslu tedy víme, jak to má fungovat, a jdeme na implementaci. Zde si bohatě vystačíme s detekcí, že jsme offline. Jak ale poznat, kdy je prohlížeč skutečně offline? Pomůže nám v tom nějak AppCache? Co se stane když uživatel vyvolá pomocí „F5“ refresh stránky?

Přesuňme se nyní do části „Jak na to“, kde si jednotlivé postupy popíšeme.

Detekce offline v prohlížeči

Na první pohled jednoduchá záležitost ve skutečnosti jednoduchá vůbec není.

Za prvé, existují vlastnosti DOM modelu, které vás informují, zda jste online, nebo offline. Velmi výstižný popis najdete na MDN. Můžete použít:

  • navigator.onLine - prozradí aktuální stav prohlížeče,
  • eventy online and offline, ke kterým můžete jednoduše přidat handler, který se vykoná při změně stavu.

Podle caniuse.com jsou tyto vlastnosti bohatě podporovány. Problém vyřešen? Ne tak docela.

Hlavní potíž, na kterou totiž narazíte, je, že prohlížeč sám od sebe nedělá nějaké pravidelné testy, zda je online nebo offline. On na to většinou přijde až tehdy, když se volá nějaký síťový požadavek, a to už může být trochu pozdě, pokud aplikace spoléhá na jeho provedení.

Narazil jsem během pátrání na knihovnu Offline.js a inspiroval se jejím modelem fungování. Knihovna je založena na metodě poněkud hrubé, ale elegantní - posílání testovacího, krátkého požadavku a detekce úspěchu. To v principu vypadá takto:

Offline detekce
Obrázek: model fungování hrubé detekce online / offline.

Pokud prostě potřebujete (jako já v našem příkladě) jen vědět, že při tom či tom AJAX požadavku aplikace spadla do „offline“, nebo že se požadavek vykonal v pořádku (a pak jde tedy o stav „online“), nemusíte využívat Offline.js a napíšete si ten kód jednoduše sami. Viz moje implementace (ES6):

let offline = false;
let xhr = new XMLHttpRequest();
xhr.open('HEAD'), OFFLINE_TEST_IMAGE + "?s=" + generateRandomId(), false);
try {
  xhr.send();
} catch(e) {
  offline = true;
}	


Proč ta na první pohled komplikovaná řádka s udáním cesty požadavku? OFFLINE_TEST_IMAGE představuje cestu k testovacímu obrázku (například favicona - na velikosti nezáleží, děláme jen HEAD požadavek) a funkce generateRandomId() vytvoří náhodný řetězec, čímž zabráníme prohlížeči v cachování tohoto dotazu.

Takový jednoduchý kousek kódu pak tvoří něco, co jsem nazval OfflineBlock - jde o metodu, kterou volají všechny asynchronní požadavky a která vykoná AJAX dotaz pouze, pokud offline == false.

Toto řešení je triviální a funguje skvěle, má však jednu nevýhodu. Pokud uživatel udělá refresh stránky, o všechno přijde a objeví se mu chybová hláška.

Proto je to řešení naivní a vyžaduje poučené uživatele. V naší konkrétní realizaci jsme například zvýraznili v aplikaci, že nastal stav „offline“ a po kliknutí na nápis vyskočí dialog, který nabízí „retry“ poslední akce, avšak s aktuálními daty (aby si nepřepsal dosud vytvořené změny). Zde skvěle vychází vstříc fakt, že máme stav aplikace neustále k dispozici v Redux stavovém stromu.

Application Cache (AppCache)

Trochu zavádějící název označuje metodu, kterou ve standardu HTML5 ovládáte nastavení cachování a chování prohlížeče, když je offline. Tedy, mělo by tomu tak být, ale jak si ukážeme za chvíli, pokrýt všechny scénáře a k tomu jednoduše, to s AppCache prostě nedokážete.

Pokud chcete s AppCache pracovat, přečtěte si prosím její specifikaci opravdu důsledně. Zejména pozor na detaily. Vyhnete se tak nadávkám a nekonečnému přemítání, proč to nefunguje tak, jak má.

Pozn.: pro nyní ignorujte, že všude vidíte varování „deprecated“. AppCache je skutečně považována za přežitou a to z dobrých důvodů. Ale smysluplná náhrada v tomto okamžiku (stav k listopadu 2017) prostě neexistuje.

Hlavní princip leží na definici speciálního souboru AppCache Manifest, což je libovolný soubor, který váš server pošle s mime-type: text/cache-manifest – mimochodem, pokud tomu tak není, v Apache zajistíte tímto nastavením v .htaccess:

#vsechny soubory .appcache - pripona muze byt jina
AddType text/cache-manifest .appcache 	

Tento soubor musíte přidružit k vaší stránce takto:

<html manifest="/manifest.appcache">	

 

Při nahrání vaší HTML stránky pod konkrétní adresou pak dojde ve vašem prohlížeči k propojení: na dané adrese je k dispozici manifest soubor, který popisuje, jak se pracuje s nahráváním dokumentů.

Struktura AppCache souboru

Soubor musí začínat textem:

CACHE MANIFEST	

a následuje několik volitelných sekcí. Sekce se vždy uvodí názvem sekce velkými písmeny a dvojtečkou. Pod ně píšete jednotlivé adresy dokumentů, každý na nový řádek.

První je sekce CACHE: – uvádí seznam souborů, které mají být cachovány, a tedy budou fungovat i offline. Adresy jsou relativně k umístění souboru s manifestem (důležitý detail!). Dále není nutné uvádět stránky, ve kterých je manifest uveden v HTML - tyto jsou asociovány automaticky.

CACHE:
js/index.js
css/styles.css

 

Další sekce, NETWORK: – říká, že se tento soubor vždy musí nahrát ze serveru.

NETWORK:
data/live.json

 

Poslední sekce, FALLBACK – slouží k určení záložního souboru v případě, že síťový požadavek na soubor spadne. Jako jediný se skládá z trochu jiné syntaxe: zdrojový soubor a mezerou oddělený soubor k nahrazení.

FALLBACK:
images/header.jpg images/header_offline.jpg

Záložní soubor se pochopitelně při zpracování manifestu uloží do cache, aby to celé mohlo fungovat.

 

Problémy s AppCache

Výborný článek s problémy AppCache popsal Jake Archibald v článku Application Cache is a Douchebag. V roce 2012, přátelé! Z něj si vypůjčíme několik nejzásadnějších problémů, které je nutné umět řešit.

  • Soubory jsou cachovány pořád, i když jste offline. Proto se nehodí pro případy, kdy jeden soubor může mít rozdílný obsah online / a offline. Vyšlo mi, že nejlepší je mít zdroje pro online / offline na jiných místech nebo jinak nazvané.
  • Necachované zdroje se nenahrají na cachované stránce, i když jste online. Viz předchozí bod - opravdu se vyplatí oddělovat online a offline zdroje s rozmyslem.
  • Application Cache se updatuje pouze, když se soubor s manifestem fyzicky změní. Lze upravit komentářem v manifestu, který se při buildu automaticky regeneruje.
  • Application Cache není cache v prohlížeči. Ten stále aplikuje i svoji cache, na základě HTTP hlaviček. Často se tak může cachovat obsah, který jste specifikovali pomocí NETWORK sekce, že se vždy má brát jako online.

AppCache API

V JavaScriptu můžete využít AppCache API a to je zajímavá věc, která stojí za pozornost. Snadno lze detekovat stav s ohledem na AppCache:

window.applicationCache.status == 2 
	// checking - kontroluje novejsi verzi manifestu

AppCache dále vyvolává několik eventy v době, kdy se mění applicationCache.status - lze tak na ně naslouchat a vázat svoje další kroky.

No a nakonec jsou k dispozici funkce, kterými můžete s AppCache manipulovat:

window.applicationCache.update() 
	// vyvolá update AppCache
window.applicationCache.swapCache() 
	// aktualizuj za novou AppCache, je-li dostupná
	// (jinak by se to provedlo až po reloadu)

Závěrem

Probrali jsme tedy důvody, proč mít na svém webovém projektu podporu offline provozu, řekli si něco o detekci offline a Application Cache. V dalším díle se podíváme na zoubek Service Workers, což je stále tak trochu hudba budoucnosti, ale s obrovskými možnostmi.

Honza Malý
maly@kurzor.net
+420 722 211 443
Honza se specializuje na návrh webů a UI, věnuje se také vývoji.

Seriál: Offline

Offline v prohlížeči 2. - Service Workers

Nebylo by skvělé mít kontrolu nad aplikační cache přímo, přes programovatelné rozhraní? K tomu jsou určeny Service Workers.

Honza Malý25.4.2018