Der HTML5 AppCache (auch bekannt als HTML5 Manifest oder Offline-Manifest) bietet eine komfortable und auch sehr einfache Möglichkeit ganze Webseiten oder WebApps offline auf dem Computer des Besuchers zu speichern. Insofern der Browser die entsprechenden Funktionen unterstützt, bekommt der Besucher von dem Download im Hintergrund nicht einmal etwas mit.
Er kann sich aber in dem Moment freuen, in dem er offline geht oder Unterwegs z.B. die Internetverbindung abreisst und er die Webseite oder WebApp auch weiterhin verwenden kann.
Ideal ist der HTML5 AppCache vor allem für WebApps die ihre Funktionen mittels JavaScript zur Verfügung stellen und die Daten der Webseite z.B. mit dem Local-Storage des Browsers syncen.
Ebenfalls können Webseiten die eine Dokumentation oder ähnliches beinhalten davon profitieren, da der Nutzer diese dann auch offline verwenden kann. Generell kann die Funktionen natürlich benutzt werden wie man möchte, aber gerade in den genannten Fällen macht es besonders viel Sinn.
Nicht ganz so ideal sind Webseiten die unbedingt PHP oder andere serverseitige Sprachen benötigen um Funktionen auszuführen. Wenn es eine gerenderte und ebenso speicherbare HTML-Ausgabe der Scripte gibt, ist es natürlich kein Problem. Nicht funktionieren würde aber z.B. eine PHP-Datei die ein Kontaktformular validieren und danach versenden soll (ist natürlich nur ein Problem, so lange der Nutzer offline ist).
Wie funktioniert der HTML5 AppCache
Bevor du den AppCache nun direkt in jede Webseite einbindest, solltest du dir Gedanken über die Funktion des AppCaches machen, denn hier gibt es für „normale Webseiten“ durchaus auch ein paar Tücken.
Also wie funktioniert der AppCache nun genau? Allgemein kommt der AppCache dem normalen Browser-Cache schon sehr nah, der Browser öffnet wie immer die Webseite und sucht nach einem Verweis auf ein entsprechendes AppCache Manifest, ist dieses vorhanden, werden alle darin enthaltenen Dateien im Hintergrund heruntergeladen, und zwar erst nachdem die angeforderte Seite fertig geladen wurde. Der eigentliche Ladevorgang wird also beim ersten Aufrufen weder positiv noch negativ beeinflusst.
Hat der Browser alle angegebenen Dateien heruntergeladen, lädt er diese nur noch aus dem AppCache und nicht mehr vom Server. Öffnet man also nach dem Abruf der ersten Seite eine weitere Unterseite, ist diese quasi sofort da und wird komplett aus dem Cache geladen. Nun kommen wir zu unserem ersten Problem:
Problem-Beispiel 1:
Wenn sich auf der Webseite online die CSS-Datei oder Inhalte ändern, dann bekommt der Benutzer weiterhin die alten Dateien aus dem AppCache ausgeliefert und der AppCache hat kein Verfallsdatum. Der Besucher könnte also theoretisch selbst nach einem Jahr noch die alte Webseite sehen, die aus seinem Cache gebildet wird. Der AppCache wird erst dann neu erzeugt, wenn sich die Manifest-Datei ändert und auch dann ist die Webseite nicht sofort mit dem aktuellen Stand zu sehen. Der Benutzer müsste nach dem AppCache Update erst einmal die Seite mit dem „Aktualisieren-Button“ in seinem Browser neu laden. Dieser Prozess lässt sich zwar mit JavaScript steuern, dürfte aber für die normale Durchschnittswebseite schon nicht mehr unbedingt sinnvoll sein.
Problem-Beispiel 2:
Ein weiteres Problem, dass in der Funktionsweise des AppCaches auftreten könnte, wäre folgendes: Im Manifest werden zwei Dateien geladen: index.html und style.css – weitere Angaben gibt es im Manifest nicht. Nun sind auf der Webseite aber eigentlich noch zwei weitere JavaScripte eingebunden, diese werden z.B. durch irgendwelche Social Widgets nachgeladen. Am wahrscheinlichsten wäre jetzt die Vermutung, dass die nachgeladenen Dateien einfach online bezogen werden?! Genau das ist aber nicht der Fall! Ist die Datei nicht im Manifest definiert, liegt ebenfalls nicht im AppCache und es gibt auch keinen Fallback, dann behandelt der Browser das ganze so, als würde die Datei gar nicht existieren, obwohl sie online abrufbar wäre!
Dieses letzte Problem lässt sich zwar mit entsprechenden Einstellungen im Manifest verhindern, kann aber schnell zu einem Problem werden, wenn viele externe Scripte eingebunden werden und diese weitere unbekannte Dateien nachladen (ein Beispiel wären hier die bereits genannten Social Widgets).
Wenn die genannten Problemfälle, die sich durchaus auch vermeiden lassen, kein Hindernis darstellen, ist der AppCache eine großartige Funktion um Offline-Apps zu bauen oder Webseiten zu beschleunigen und den Traffic zu reduzieren oder Dateien vorzuladen.
Ein letzter wichtiger Hinweis: Im Normalfall kann der AppCache der meisten Browser maximal 5MB speichern, daher Obacht mit großen Bildern!
Struktur der Manifest-Datei und Integration in die Webseite
Beschäftigen wir uns als erstes mit der Verknüpfung von Manifest und HTML-Datei, diese ist relativ simpel und schnell erledigt. Die Manifest Datei mit der Endung .appcache wird mittels HTML-Tag verknüpft, dass könnte dann z.B. so aussehen:
...
Das war dann auch schon der ganze Zauber, mehr wird nicht benötigt um ein Cache-Manifest zu integrieren.
Wichtig: Jede Seite die über einen Manifest-Verweis verfügt wird automatisch dem AppCache hinzugefügt, egal ob sie dort angegeben ist oder nicht.
Schauen wir uns als nächstes die einfachste Version einer Cache-Manifest Datei an, diese beinhaltet nur eine Liste mit Dateien die lokal gespeichert werden sollen:
Einfache Variante eines Cache-Manifestes:
CACHE MANIFEST
# Das hier ist ein Kommentar!
# Es ist sinnvoll die Version und Änderungsdatum am Anfang zu positionieren, z.B.:
# 2014-01-26:v1
CACHE:
# Dateien auf entfernten Servern
//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700
//html5shiv.googlecode.com/svn/trunk/html5.js
//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
# Lokale Dateien
unterseite1.html
unterseite2.php
assets/css/bootstrap.min.css
assets/css/mycodestock/jquery-ui-1.10.3.min.css
assets/css/main.css
assets/img/background-berlin_ipad.jpg
assets/img/background-berlin.jpg
assets/img/background-new-york_ipad.jpg
Da der AppCache nur neu angelegt werden kann, wenn sich das Manifest ändert, macht es Sinn ein Änderungsdatum in Kommentarform einzubetten. Bei Datei-Updates einfach das Datum und ggf. ein paar Zeilen im Manifest anpassen und der Browser erneuert den AppCache.
In dem obigen Beispiel werden alle gelisteten Dateien lokal gespeichert, es gibt keinen Fallback und Dateien die hier nicht gelistet sind, werden später vom Browser behandelt, als wären sie nicht da. Auch dann, wenn sie z.B. in der Seite selber korrekt verlinkt sind und auch online abrufbar wären.
Um Fehler zu vermeiden sollte man dem Browser daher noch ein paar mehr Informationen übermitteln, die vielleicht auch etwas redundanter sind, dazu kommen noch ein paar weitere Bereiche in die Manifest-Datei.
In dem Beispiel oben haben wir lediglich den Part „CACHE:“ verbaut (die Überschrift „CACHE:“ ist in dem Beispiel oben sogar optional und könnte entfernt werden), es gibt allerdings noch zwei weitere Bereiche die in einem AppCache Manifest eingetragen werden können, und zwar „NETWORK:“ und „FALLBACK:“. Bevor wir uns dem Beispiel widmen, kurz ein paar Worte zur Bedeutung der drei Bereiche.
CACHE: Hier werden alle Ressourcen gelistet, die der Browser in den AppCache laden soll.
NETWORK: Hier werden alle Ressourcen gelistet, die online abgerufen werden sollen.
FALLBACK: In diesem Bereich können Fallbacks, z.B. für dynamische Scripte eingetragen werden: Statt dynamisch.php soll z.B. statisch.html geladen werden (greift nur, wenn der Nutzer auch wirklich offline ist)
Nun ein vollständiges Beispiel eines Cache-Manifestes:
CACHE MANIFEST
# 2014-01-26:v1
CACHE:
unterseite1.html
unterseite2.php
//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700
//html5shiv.googlecode.com/svn/trunk/html5.js
//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js
assets/css/bootstrap.min.css
assets/css/mycodestock/jquery-ui-1.10.3.min.css
assets/css/main.css
assets/img/background-berlin_ipad.jpg
assets/img/background-berlin.jpg
assets/img/background-new-york_ipad.jpg
NETWORK:
*
http://*
https://*
FALLBACK:
/index.php /statische-alternative.html
/formular.php /offline.html
Das Beispiel sollte soweit selbsterklärend sein, ein Bereich ist allerdings interessant, daher sehen wir uns diesen noch einmal etwas genauer an:
NETWORK:
*
http://*
https://*
Dieser Part im Bereich Network sorgt dafür, dass Ressourcen die:
- nicht im AppCache verfügbar sind
- oder auf externen Servern liegen und nicht explizit im Bereich Cache gelistet wurden
von den entsprechenden Online-Ressourcen geladen werden. Der Browser prüft also erst ob die Daten im AppCache liegen und greift alternativ auf die Online-Ressourcen zurück, falls ersteres nicht der Fall ist (für diesen Fallback wird natürlich eine Internetverbindung vorausgesetzt).
HTML5 AppCache JavaScript API
Mit folgendem Code kann der aktuelle Cache Status abgefragt werden:
var appCache = window.applicationCache;
switch (appCache.status) {
case appCache.UNCACHED: // UNCACHED == 0
return 'UNCACHED';
break;
case appCache.IDLE: // IDLE == 1
return 'IDLE';
break;
case appCache.CHECKING: // CHECKING == 2
return 'CHECKING';
break;
case appCache.DOWNLOADING: // DOWNLOADING == 3
return 'DOWNLOADING';
break;
case appCache.UPDATEREADY: // UPDATEREADY == 4
return 'UPDATEREADY';
break;
case appCache.OBSOLETE: // OBSOLETE == 5
return 'OBSOLETE';
break;
default:
return 'UKNOWN CACHE STATUS';
break;
};
Um den Cache zu aktualisieren und den Nutzer über entsprechende Updates zu informieren kann folgender JavaScript Code verwendet werden:
// Prüfen ob bei Seitenaufruf eine neue Version verfügbar ist
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser hat einen neuen AppCache gebildet
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
} else {
// Hier kann etwas gemacht werden, wenn der Cache nicht geupdated wurde
}
}, false);
}, false);
Übersicht über mögliche Events der AppCache API in JavaScript:
function handleCacheEvent(e) {
//...
}
function handleCacheError(e) {
alert('Error: Cache konnte nicht erzeugt werden!');
};
appCache.addEventListener('cached', handleCacheEvent, false);
appCache.addEventListener('checking', handleCacheEvent, false);
appCache.addEventListener('downloading', handleCacheEvent, false);
appCache.addEventListener('error', handleCacheError, false);
appCache.addEventListener('noupdate', handleCacheEvent, false);
appCache.addEventListener('obsolete', handleCacheEvent, false);
appCache.addEventListener('progress', handleCacheEvent, false);
appCache.addEventListener('updateready', handleCacheEvent, false);