TakéOffline mode, Offline-first, Režim bez připojeníPokročilý
Definice
Offline režim je schopnost aplikace fungovat i bez připojení k síti: data i kód má uložené lokálně v zařízení, změny zapisuje do místního úložiště a odesílá je na server až ve chvíli, kdy se spojení obnoví. Uživatel tak může číst obsah i pořizovat záznamy bez ohledu na dostupnost sítě.
Než se na to spolehnete: Kvóty offline úložiště v prohlížečích se liší podle enginu i verze, proto je v FAQ uvedeno bez konkrétních čísel. sameAs je prázdné, protože pro obecný pojem „offline režim“ neexistuje jednoznačný odpovídající článek na Wikipedii.
Co všechno musí být offline dostupné
Offline režim není jedna funkce, ale tři oddělené vrstvy. První je aplikační skořápka: HTML, JavaScript, CSS, fonty a ikony, tedy věci, které se mění zřídka a dají se předem uložit do cache. Druhou vrstvou jsou data: seznamy zakázek, produktů, zpráv. Třetí a nejtěžší jsou zápisy, které uživatel udělá bez sítě a které se musí později dostat na server.
Na webu tuhle roli plní service worker, který zachytává síťové požadavky a rozhoduje, zda je vyřídí z cache, nebo je pustí na server. Data se ukládají do IndexedDB, u nativních aplikací obvykle do SQLite. Kombinace service workeru a lokální databáze je podstatou Progressive Web App.
Kde vzniká problém: synchronizace zápisů
Čtení offline je poměrně snadné. Skutečná složitost začíná ve chvíli, kdy dva lidé změní stejný záznam, každý ve svém odpojeném zařízení. Server pak dostane dvě verze a musí rozhodnout, která platí. Používané strategie jsou:
- Last write wins: rozhoduje časové razítko. Jednoduché, ale tiše zahazuje cizí práci.
- Verzování a odmítnutí: klient posílá verzi, kterou editoval; server konflikt vrátí a uživatel ho vyřeší ručně.
- Operační log: neposílá se výsledný stav, ale seznam operací (přidej položku, změň množství), které jdou přehrát po sobě.
- CRDT: datové struktury navržené tak, aby se souběžné změny slučovaly deterministicky bez zásahu uživatele.
Volba není technická drobnost. U poznámkové aplikace stačí last write wins, u skladové evidence nebo zdravotnické dokumentace je ztráta zápisu nepřijatelná.
Proč se offline režim navrhuje od začátku
Dodatečné doplnění offline režimu do hotové aplikace bývá dražší než původní vývoj. Aplikace stavěná online-first předpokládá, že server přidělí ID, ověří dostupnost a vrátí aktuální stav. Offline-first přístup tohle obrací: identifikátory generuje klient (typicky UUID), stav je pravdou lokálně a server je jen místem, kde se stavy slévají. Změnit tenhle předpoklad znamená přepsat datový model, ne přidat knihovnu.
Kdy offline režim nemá smysl
Ne každá aplikace ho potřebuje. Bankovní zůstatek, dostupnost letenky nebo aktuální cena akcie nesmí být zastaralé, a tak je lepší poctivě zobrazit chybu než zavádějící stará data. Rozumný kompromis je částečný offline režim: přečtené obrazovky zůstanou dostupné jen pro čtení, s viditelným časem poslední aktualizace a zablokovanými akcemi, které vyžadují server.
Detekce stavu připojení
Vlastnost navigator.onLine a události online a offline říkají jen to, že zařízení má nějaké síťové rozhraní. Hotelová Wi-Fi s přihlašovací stránkou nebo vypadlý backend se takto nepoznají. Spolehlivá detekce je až selhání skutečného požadavku, proto se stav připojení odvozuje z výsledků volání API, ne pouze z hodnoty příznaku.
Co uživatel musí vidět
Offline režim je hlavně otázkou návrhu rozhraní: uživatel potřebuje vědět, že je offline, které jeho změny čekají na odeslání a kdy naposledy se data aktualizovala. Tichá fronta bez zpětné vazby vede k tomu, že člověk odinstaluje aplikaci i s neodeslanými záznamy.
Příklady z praxe
Terénní technik bez signálu
Servisní aplikace stahuje ráno seznam zakázek do SQLite v telefonu. Technik v suterénu bez signálu vyplní protokol, vyfotí zařízení a uloží podpis; záznam dostane lokální UUID a příznak nesynchronizováno. Po návratu do dosahu sítě fronta odešle protokoly i fotky a server je přijme právě jednou, protože UUID slouží jako idempotenční klíč.
Fronta zápisů v IndexedDB
Webová aplikace nezapisuje přímo na API, ale do fronty v IndexedDB. Odesílací rutina se spustí při startu aplikace a při události online; každý pokus buď položku odstraní, nebo ji ponechá pro další kolo. Díky stabilnímu clientId nevzniknou duplicity ani při opakovaném odeslání.
async function enqueue(op) { const db = await openDB('sync'); await db.put('queue', { ...op, clientId: crypto.randomUUID(), at: Date.now() }); flush().catch(() => {}); // pokus hned, jinak později } async function flush() { const db = await openDB('sync'); for (const item of await db.getAll('queue')) { const res = await fetch('/api/records', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': item.clientId }, body: JSON.stringify(item) }); if (res.ok) await db.delete('queue', item.clientId); else break; // zachovej pořadí operací } } window.addEventListener('online', () => flush());
Časté omyly
- MýtusStačí zapnout cache a aplikace bude fungovat offline.
- Ve skutečnostiCache vyřeší jen načtení souborů a čtení dat. Offline režim stojí a padá s tím, co se stane se změnami pořízenými bez sítě: potřebuje frontu zápisů, idempotentní API a pravidlo pro řešení konfliktů.
- Mýtusnavigator.onLine spolehlivě řekne, jestli je uživatel online.
- Ve skutečnostinavigator.onLine hlásí pouze existenci síťového rozhraní. Zařízení připojené k captive portálu nebo k síti s nedostupným backendem je stále hlášeno jako online, takže skutečný stav se pozná až podle výsledku reálného požadavku.
- MýtusOffline režim je záležitost nativních aplikací, web to neumí.
- Ve skutečnostiService worker, IndexedDB a Cache Storage umožňují plnohodnotný offline provoz i v prohlížeči. Rozdíly zůstávají v limitech úložiště, v možnosti běhu na pozadí a v tom, jak agresivně systém data odpojené aplikace uklízí.
Časté dotazy
- Kolik dat lze v prohlížeči uložit pro offline režim?
- Kapacita offline úložiště v prohlížeči není pevně daná. Prohlížeče přidělují kvótu podle volného místa na disku a podle toho, jak často uživatel web navštěvuje; typicky jde o podíl z dostupného prostoru, nikoli o pevný limit v megabajtech. Skutečnou hodnotu zjistí aplikace přes Storage API. Data mohou být při nedostatku místa smazána, pokud si web nevyžádá trvalé úložiště. Pro velké objemy, například mapové podklady nebo fotodokumentaci, se proto vyplatí stahovat data po částech a nabídnout uživateli ruční správu stažených balíčků.
- Jak zajistit, aby se offline zápisy neuložily na serveru dvakrát?
- Duplicitám v offline režimu se předchází idempotencí. Klient vygeneruje identifikátor operace už v zařízení, typicky UUID, a posílá ho s každým pokusem o odeslání, například v hlavičce Idempotency-Key. Server si zpracované identifikátory pamatuje a při opakovaném doručení vrátí původní výsledek místo vytvoření nového záznamu. Tento postup je nutný, protože fronta nikdy nemá jistotu, zda předchozí požadavek serverem prošel a odpověď se jen ztratila cestou zpět.
- Je bezpečné držet citlivá data v offline úložišti?
- Offline úložiště zvyšuje riziko úniku, protože data zůstávají v zařízení i po zavření aplikace. Doporučuje se ukládat jen nezbytný výřez dat, nikoli celou databázi, mazat lokální obsah při odhlášení a spoléhat na šifrování celého disku, které nabízejí mobilní operační systémy. Přihlašovací tokeny patří do systémového bezpečného úložiště, ne do IndexedDB. U zdravotnických nebo osobních údajů je navíc nutné posoudit uložení dat v zařízení v rámci GDPR a popsat ho v dokumentaci zpracování.
- Jak testovat offline režim, aby se chyby našly před nasazením?
- Testování offline režimu vyžaduje víc než vypnutí Wi-Fi. Kromě úplného výpadku je potřeba simulovat pomalou a ztrátovou síť, přerušení uprostřed odesílání a captive portál, který vrací HTML místo odpovědi API. Vývojářské nástroje prohlížeče umožňují omezit propustnost i vypnout síť pro konkrétní záložku. Automatizované testy by měly ověřit, že fronta přežije restart aplikace, že se pořadí operací zachová a že souběžná editace stejného záznamu skončí definovaným výsledkem, ne tichou ztrátou dat.
Zdroje
- Service Worker API(otevře se v novém okně)
- IndexedDB API(otevře se v novém okně)
- Service Workers(otevře se v novém okně)
- SQLite Documentation(otevře se v novém okně)
- Conflict-free replicated data type(otevře se v novém okně)