Ich wollte etwas über progressive web apps mit service workern schreiben. Diese service workern stellen Webseiten auch offline zur Verfügung. Seit April 2018 macht hier der MacOS Safari mit. Sie arbeiten im Verborgenen. Nur per Debugger können Sie sie sehen. Im Chrome Browser erreichen Sie den Debugger über das Menü „more Tools/developer tools“. Wenn Sie im Tab application den Eintrag „service worker from other domains“ aufklappen, Sie die Domains, die bei einem Besuch ihre service woker installiert haben. Ich staunte nicht schlecht. Es gab schon eine Menge an Webseiten, die so etwas gemacht haben.
Gefragt werden Sie nicht, ob Sie diese in Ihrem Browser haben wollen. Bei Cookies ist das anders. Obwohl Cookies nur Datenpakete sind und nur durch Software, die auf anderen System läuft, gefährlich werden können. Aber diese Javascript Sachen sind Programme und könnten durchaus auch schädlich sein. In jedem Fall sollte diese Software nur ausgeführt werden, wenn auch die zugehörige Webseite wird. Falls dann ein unbedarfter Softwerker Mist programmiert hat, leiden eben die Webseiten des zugehörigen Domains.
Ich wollte sicher gehen und ein einfaches Lehrbuchbeispiel um log Einträge erweitern, damit jeder den Javascript Code bei der Ausführung beobachten kann.
Das Beispiel aus dem Lehrbuch ließ sich einfach übertragen. Ein service worker läuft in einem eigenen Thread ab. Minimal sollte der worker drei Events beherrschen:
- install Event
Der worker wird zum ersten Mal installiert. Mein worker lädt schon einmal pwa/index.html. - activate Event
Der worker wurde installiert und wird aktiviert, was immer das heißt. - fetch Event
Der Browser möchte Zugriff auf das Netz. Der worker prüft, ob er etwas aus dem Cache holt oder das Netz fragt. Ich gebe hier immer den Cache Inhalt zurück.
Alle drei Event protokollierten ihren Aufruf mit einer Ausgabe in console.log. Für den ersten Teil war das schon genug.
Das Protokoll
Ein Leser will nicht in den Developer Tools sehen, wie der service worker arbeitet. Das wollte ich ihm auf einer Webseite anzeigen. Es sollte so sein, dass wenn der Rechner um 12:00 Uhr hochgefahren ist, der Browser um 12:10 aufgerufen wird und erst um 12:20 gawehns.de/pwa besucht wird, die Meldungen auf der Webseite zeigen, ob vor 12:20 etwas passiert war.
Die erste Idee war die Meldungen in den LocalStorage einzutragen. Da beschwerte sich Chrome! Die Doku wies auch darauf hin, dass synchrone Schnittstellen nicht gehen.
Ich musste IndexedDb verwenden. Das war leichter gesagt, als getan. Die Schnittstelle ist hier asynchron. Wenn ich hier eine Funktion aufrufe, ist das Ergebnis ein Aufruf, der auf Beendigung wartet. Ich kann Funktionen setzen, die bei Erfolg oder Misserfolg ausgeführt werden. Wie sollte das umgesetzt werden?
Wenn ich das am Anfang von sw.js mache, muss ich hoffen, dass es rechtzeitig fertig wird. Also wollte ich im install und auch im activate Event die IndexdDb öffnen und einrichten. Aber auch hier gilt, dass nun der Ablauf warten muss, bis die Datenbank geöffnet und, beim ersten Aufruf, initialisiert ist. Bei einem Promise (dt. Versprechen) wird bei Erfolg oder auch Misserfolg eine Funktion ausgeführt. Die Erfolgsfunktion erzeugt dann eine Funktion, die Meldungen in die Datenbank schrieben kann. Leider waren die Aufrufobjekte von IndexedDb keine Promises. Obwohl es da wohl eine Erweiterung geben würde, die ich aber nicht auch noch laden wollte.
Ich lernte Promises selber zu programmieren und baute sie ein. Der worker hing dann in der Installation. Die Beschreibungen vom event.waituntil bezogen sich alle auf die Cache Schnittstelle. Bei der IndexedDb muss beim ersten Anlegen zweimal gewartet werden. Diese Lösung schien mir eine Sackgasse zu sein.
Da bot sich eine Implementierung per Singleton an. Die logIt Funktion schreibt in die Datenbank, wenn sie geöffnet ist. Ist sie noch nicht angelegt, so legt sie eine Datenbank an. Eine anonyme Funktion trägt dann die Meldung ein, wenn die Datenbank offen ist. Obwohl die Meldung im Funktionsrumpf angelegt wurde, bleibt sie erhalten, bis die anonyme Funktion ausgeführt wurde. Javascript hat ein wunderbares Speichermodell!
Die Ergebnisse
Das Javascript wird nur dann ausgeführt, wenn der Anwender die Webseite besucht. So konnte ich es bei MacOS high sierra und den Browsern Chrome und Safari sehen. Wenn sich ihr System anders verhält, teilen Sie bitte in den Kommentaren mit.
Unter https://www.gawehns.de/pwa sehen Sie das Panel mit den Meldungen. Beim ersten Besuch sind die Meldungen leer, da Seite vor dem service worker lädt. Drücken Sie einfach den refresh button.
Den Quellcode für den service worker finden Sie hier https://www.gawehns.de/pwa/sw.js.
Wer „richtige“ service worker sehen will, kann sie sich mit den developer tools von Chrome ansehen. Unter „Application“ sehen Sie unter anderem die service worker auch der von „anderen domains“.
Ich lernte bei www.kaufda.de/webapp die workbox API von google kennen. Zunächst wunderte ich mich, dass der javascript code nicht per click in einem neuen Tab dargestellt wird. Per wget musste ich mir das Ding herunterladen. Es enthält nur Datenstrukturen. Hochinteressant fand ich folgendes Konstrukt:
const ROUTE_BLACKLIST = [
/partner=.*$/gi,
/token=.*$/gi,
// Preview relies on preview value in HTML so can't use cache
/preview=true.*$/gi,
// DBC relies on preview value in HTML so can't use cache
/\/dbc\/.*$/gi,
// CMS Preview relies on preview value in HTML so can't use cache
/\/preview\/.*$/gi,
];
In einer Blacklist hätte ich Strings erwartet. Was war das denn?
Das musste ich ausprobieren. Im Debugger sah ich dann, was es war. Im Prototyp stand etwas von RegExp. Es handelte sich um reguläre Ausdrücke. Da waren Profis am Werk.
Diese Scripte leisten die eigentliche Arbeit in der kaufda.de/webapp:
- https://staticweb01.kaufda.de/bwa-assets/precache-manifest.a75d0f030f127d3aedc1b16163ccf477.js
Das Manifest sagt, was in den cachen kommt und ob google analytics auch die Zugriffe auf diese Seiten zählen soll. - https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js
Minifizierter javascript, der die Schnittstelle oder besser das Framework startet.
Google stellt mit Workbox ein Framework für die Arbeit mit service workern zur Verfügung. Das werde ich versuchen für PWAs zu verwenden. Eine richtige Idee für eine Anwendung habe ich noch nicht so genau. Vielleicht baue ich ja einen Push Service, der Cookies aus Unix System III pusht.