diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:41:15 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:41:15 -0500 |
| commit | 4b1a9203c547c019fc5398082ae19a3f3d4c3efe (patch) | |
| tree | d4a40e13ceeb9f85479605110a76e7a4d5f3b56b /files/de/web/api/service_worker_api | |
| parent | 33058f2b292b3a581333bdfb21b8f671898c5060 (diff) | |
| download | translated-content-4b1a9203c547c019fc5398082ae19a3f3d4c3efe.tar.gz translated-content-4b1a9203c547c019fc5398082ae19a3f3d4c3efe.tar.bz2 translated-content-4b1a9203c547c019fc5398082ae19a3f3d4c3efe.zip | |
initial commit
Diffstat (limited to 'files/de/web/api/service_worker_api')
| -rw-r--r-- | files/de/web/api/service_worker_api/index.html | 280 | ||||
| -rw-r--r-- | files/de/web/api/service_worker_api/using_service_workers/index.html | 507 |
2 files changed, 787 insertions, 0 deletions
diff --git a/files/de/web/api/service_worker_api/index.html b/files/de/web/api/service_worker_api/index.html new file mode 100644 index 0000000000..54e3e1d86e --- /dev/null +++ b/files/de/web/api/service_worker_api/index.html @@ -0,0 +1,280 @@ +--- +title: Service Worker API +slug: Web/API/Service_Worker_API +tags: + - API + - Offline + - Referenz + - Service-Worker + - Worker +translation_of: Web/API/Service_Worker_API +--- +<div> +<p>{{ServiceWorkerSidebar}}</p> + +<p>{{ SeeCompatTable() }}</p> + +<p class="summary">Service-Worker verhalten sich im Wesentlichen wie Proxy-Server, welche zwischen der Webanwendung bzw. dem Browser und dem Netzwerk (sofern dieses verfügbar ist) sitzen. Sie sollen u. a. das Erstellen wirksamer Offline-Erfahrungen ermöglichen und können Netzwerkanfragen abfangen und passende Maßnahmen abhängig davon ergreifen, ob das Netzwerk verfügbar ist und ob der Server veränderte Ressourcen enthält. Sie erlauben außerdem den Zugriff auf Push-Benachrichtigungen und Background Sync APIs.</p> +</div> + +<h2 id="Konzepte_von_Service-Workern_und_deren_Nutzung">Konzepte von Service-Workern und deren Nutzung</h2> + +<p>Ein Service-Worker ist ein ereignisgesteuerter <a href="/de/docs/Web/API/Worker">Worker</a>, der an einem Ursprung und einem Pfad registriert ist. Sie ist eine JavaScript-Datei, die in Abhängigkeit zu einer Webseite steht, Anfragen von Ressourcen abfängt und cached. In manchen Situationen kann es das Verhalten der Webseite beeinflussen. Ein häufiger Anwendungsfall ist die Abfrage, ob ein Netzwerk verfügbar ist, oder nicht.</p> + +<p>Ein Service-Worker läuft in einem Worker-Kontext. Es hat keinerlei Zugriff auf das DOM und läuft in einem separaten Thread, also isoliert vom JavaScript, dass die Hauptinteraktionslogik der Website steuert. Es läuft vollständig asynchron und verhindert die Ausführung anderer Scripte nicht. Daraus resultiert, dass APIs wie <a href="/en-US/docs/Web/API/XMLHttpRequest">XHR</a> oder <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage">LocalStorage</a> nicht in Service-Workern benutzt werden können.</p> + +<p>Service-Worker laufen aus Sicherheitsgründen nur über das HTTPS-Protokoll. Veränderte Netzwerkanfragen könnten "Man in the middle"-Angriffe deutlich leichter machen.</p> + +<div class="note"> +<p><strong>Hinweis: </strong>Service-Worker haben in Bereichen wie <a href="http://alistapart.com/article/application-cache-is-a-douchebag">AppCache</a> gezeigt, dass sie dort besonders gut genutzt werden können, da sie keine Vermutungen darüber treffen, was Sie machen wollen und brechen ihre Aktionen entsprechend ab. Sie haben deshalb die Kontrolle über alles.</p> +</div> + +<div class="note"> +<p><strong>Hinweis</strong>: Service-Worker basieren auf <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>. Generell sind sie abhängig von Anfragen und sie werden mit einer erfolgreichen oder einer fehlerhaften Aktion antworten. Die Architektur ist deshalb ideal dafür.</p> +</div> + +<h3 id="Registrierung">Registrierung</h3> + +<p>Ein Service-Worker wird über die {{domxref("ServiceWorkerContainer.register()")}}-Methode registriert. Falls sie erfolgreich war, wird Ihr Service-Worker vom Client heruntergeladen und versucht eine Installation/Aktivierung (siehe unten) für URLs, die innerhalb der Quelle liegen oder die URLs, die Sie vorgeben.</p> + +<h3 id="Download_Installation_und_Aktivierung">Download, Installation und Aktivierung</h3> + +<p>Ihr Service-Worker muss folgenden Lebenszyklus durchlaufen:</p> + +<ol> + <li>Download</li> + <li>Installation</li> + <li>Aktivierung</li> +</ol> + +<p>Der Service-Worker wird sofort mit heruntergeladen, sobald der Nutzer erstmals eine von Service-Workern kontrollierte Seite aufruft.</p> + +<p>Danach wird es alle 24 Stunden neu heruntergeladen. Es kann auch in kürzeren Abständen heruntergeladen werden, aber die 24 Stunden können <strong>nicht </strong>überschritten werden. Damit sollen die Ladezeiten kürzer werden.</p> + +<p>Eine Installation wird versucht, wenn die heruntergeladene Datei neu ist, also Unterschiede byteweise verglichen mit dem bestehenden Service-Worker aufweist oder es der erste Service-Worker ist, der für diese Seite heruntergeladen wurde.</p> + +<p>Wenn ein Service-Worker erstmals verfügbar ist, wird eine Installation versucht. Nach einer erfolgreichen Installation ist es aktiviert.</p> + +<p>Wenn bereits ein bestehender Service-Worker installiert wurde, wird die neue Version im Hintergrund installiert, aber noch nicht aktiviert. Zu diesen Zeitpunkt wartet der Worker, bis alle Seiten heruntergeladen wurden, die noch den alten Service-Worker nutzen. Sobald alle Seiten geladen worden sind, wird der neue Service-Worker aktiviert.</p> + +<p>Sie können ein Ereignishandler für das {{domxref("InstallEvent")}} erstellen. Standardmäßig wird der Worker für den Einsatz vorbereitet, wenn das Event feuert. Zum Beispiel beim erstellen eines Caches, bei der die Built-In Storage API verwendet wird und Assets hineingeladen werden, damit die App offline verwendet werden kann.</p> + +<p>Außerdem existiert ein <code>activate</code>-Event. Wenn es feuert, ist es ein guter Zeitpunkt die alten Caches und andere Daten zu bereinigen, die mit der vorherigen Version ihres Workers zusammenhängen.</p> + +<p>Ihr Service-Worker kann mit dem {{domxref("FetchEvent")}} auf Anfragen antworten. Sie können die Antworten auf die Anfragen verändern, wie Sie wollen. Nutzen Sie dazu die {{domxref("FetchEvent.respondWith") }}-Methode.</p> + +<div class="blockIndicator note"> +<p><strong>Hinweis:</strong> Weil <code>oninstall</code>/<code>onactivate </code>eine Weile benötigen, um ihre Aktionen abzuschließen, ist es empfehlenswert, eine <code>waitUntil</code>-Methode bereitzustellen, die, wenn <code>oninstall </code>oder<code> onactivate</code> gefeuert werden, ein <code>Promise</code> geliefert wird. Events, die der Funktion dienen, werden dem Service-Worker nicht enthalten und der <code>Promise</code> wird erfolgreich ausgeführt.</p> +</div> + +<p>Für eine komplette Anleitung, die zeigt, wie Sie Ihr erstes Beispiel erstellen, siehe <a href="/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers">Using Service Workers</a>.</p> + +<h2 id="Weitere_Anwendungsbereiche">Weitere Anwendungsbereiche</h2> + +<p>Service-Worker werden auch benutzt für:</p> + +<ul> + <li>die Synchronisation von Hintergrunddaten</li> + <li>um auf Anfragen anderer Quellen zu antworten</li> + <li>Um Updates von Daten zu erhalten, die sehr teuer in der Kalkulation sind, wie z. B. Standort-Daten, die dann in einem Datensatz verwendet werden können.</li> + <li>Das clientseitige Kompilieren von CoffeeScript aus Entwicklergründen</li> + <li>Hooks für Hintergrunddienste</li> + <li>Zum Erstellen benutzerdefinierter Templates anhand von URL-Mustern</li> + <li>Performancebeschleunigung wie z. B. das Vorladen von Bildern</li> +</ul> + +<p>In Zukunft werden Service-Worker zu weiteren nützlichen Dingen fähig sein, die sie näher an eine native App bringen. Andere Spezifikationen können und werden den Service-Worker-Kontext benutzen. Zum Beispiel:</p> + +<ul> + <li><a href="https://github.com/slightlyoff/BackgroundSync">Hintergrund-Synchronisation</a>: Ein Service-Worker kann gestartet werden, wenn keine Nutzer auf der Seite sind, um den Cache zu aktualisieren usw.</li> + <li><a href="/en-US/docs/Web/API/Push_API">Auf Push-Benachrichtigungen reagieren</a>: Ein Service-Worker kann Benachrichtigungen an Nutzer senden, um mitzuteilen, dass neuer Inhalt verfügbar ist,</li> + <li><span class="tlid-translation translation" lang="de"><span title="">Auf eine bestimmte Uhrzeit und ein bestimmtes Datum reagieren</span></span></li> + <li>Damit kann geofencing betrieben werden</li> +</ul> + +<h2 id="Schnittstellen">Schnittstellen</h2> + +<dl> + <dt>{{domxref("Cache") }}</dt> + <dd>Repräsentiert einen Speicher für {{domxref("Request")}} / {{domxref("Response")}}-Objeltpaare, die als ein Teil des {{domxref("ServiceWorker")}}-Lifecycles agieren.</dd> + <dt>{{domxref("CacheStorage") }}</dt> + <dd>Repräsentiert einen Speicher für {{domxref("Cache")}}-Objekte. Es ist das Hauptverzeichnis für {{domxref("ServiceWorker")}}. Es beinhaltet alle benannten Caches basierend auf Strings, auf die der Worker zugreifen kann.</dd> + <dt>{{domxref("Client") }}</dt> + <dd>Repräsentiert den Gültigkeitsbereich von einem Service-Worker-Client. Ein Service-Worker-Client ist entweder ein Dokument in einem Browser-Kontext oder ein {{domxref("SharedWorker")}}, welches von einem aktiven Worker gesteuert wird.</dd> + <dt>{{domxref("Clients") }}</dt> + <dd>Repräsentiert einen Container von {{domxref("Client")}}-Objekten; die hauptsächtliche Methode, um die aktiven Service-Worker-Clients anzusteuern.</dd> + <dt>{{domxref("ExtendableEvent") }}</dt> + <dd>Erweitert die Lebensdauer der <code>install </code>und <code>activate</code>-Events, die vom {{domxref("ServiceWorkerGlobalScope")}} entfernt werden als teil des Service-Worker-Lebenszyklus. Dies versichert, dass einige Events wie z. B. das {{domxref("FetchEvent")}} nicht vom {{domxref("ServiceWorker")}}, solange sie veraltete Cache-Einträge löschen usw.</dd> + <dt>{{domxref("ExtendableMessageEvent") }}</dt> + <dd>Das Event-Objekt von einem {{event("message_(ServiceWorker)","message")}}-Event, welches von einem Service-Worker gefeuert wird.</dd> + <dt>{{domxref("FetchEvent") }}</dt> + <dd>Die Parameter, die dem {{domxref("ServiceWorkerGlobalScope.onfetch")}}-Handler übergeben werden. <code>FetchEvent </code>repräsentiert eine Fetch-Aktion, die vom {{domxref("ServiceWorkerGlobalScope")}} eines {{domxref("ServiceWorker")}} entfernt wird. Es beinhaltet Information über die Anfrage und der daraus resultierenden Antwort, und stellt die {{domxref("FetchEvent.respondWith", "FetchEvent.respondWith()")}}-Methode bereit. Es ermöglicht eine willkürliche Antwort, die zurück zur kontrollierten Seite gesendet wird.</dd> + <dt>{{domxref("InstallEvent") }}</dt> + <dd>The parameter passed into the {{domxref("ServiceWorkerGlobalScope.oninstall", "oninstall")}} handler, the <code>InstallEvent</code> interface represents an install action that is dispatched on the {{domxref("ServiceWorkerGlobalScope")}} of a {{domxref("ServiceWorker")}}. As a child of {{domxref("ExtendableEvent")}}, it ensures that functional events such as {{domxref("FetchEvent")}} are not dispatched during installation. </dd> + <dt>{{domxref("Navigator.serviceWorker") }}</dt> + <dd>Returns a {{domxref("ServiceWorkerContainer")}} object, which provides access to registration, removal, upgrade, and communication with the {{domxref("ServiceWorker")}} objects for the <a href="https://html.spec.whatwg.org/multipage/browsers.html#concept-document-window">associated document</a>.</dd> + <dt>{{domxref("NotificationEvent") }}</dt> + <dd>The parameter passed into the {{domxref("ServiceWorkerGlobalScope.onnotificationclick", "onnotificationclick")}} handler, the <code>NotificationEvent</code> interface represents a notification click event that is dispatched on the {{domxref("ServiceWorkerGlobalScope")}} of a {{domxref("ServiceWorker")}}.</dd> + <dt>{{domxref("ServiceWorker") }}</dt> + <dd>Represents a service worker. Multiple browsing contexts (e.g. pages, workers, etc.) can be associated with the same <code>ServiceWorker</code> object.</dd> + <dt>{{domxref("ServiceWorkerContainer") }}</dt> + <dd>Provides an object representing the service worker as an overall unit in the network ecosystem, including facilities to register, unregister and update service workers, and access the state of service workers and their registrations.</dd> + <dt>{{domxref("ServiceWorkerGlobalScope") }}</dt> + <dd>Represents the global execution context of a service worker.</dd> + <dt>{{domxref("ServiceWorkerMessageEvent")}}</dt> + <dd>Contains information about an event sent to a {{domxref("ServiceWorkerContainer")}} target. </dd> + <dt>{{domxref("ServiceWorkerRegistration") }}</dt> + <dd>Represents a service worker registration.</dd> + <dt>{{domxref("SyncEvent")}} {{non-standard_inline}}</dt> + <dd> + <p>The SyncEvent interface represents a sync action that is dispatched on the {{domxref("ServiceWorkerGlobalScope")}} of a ServiceWorker. </p> + </dd> + <dt>{{domxref("SyncManager")}} {{non-standard_inline}}</dt> + <dd>Provides an interface for registering and listing sync registrations.</dd> + <dt>{{domxref("WindowClient") }}</dt> + <dd>Represents the scope of a service worker client that is a document in a browser context, controlled by an active worker. This is a special type of {{domxref("Client")}} object, with some additional methods and properties available.</dd> +</dl> + +<h2 id="Spezifikationen">Spezifikationen</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Spezifikation</th> + <th scope="col">Status</th> + <th scope="col">Kommentar</th> + </tr> + <tr> + <td>{{SpecName('Service Workers', '')}}</td> + <td>{{Spec2('Service Workers')}}</td> + <td>Initial definition.</td> + </tr> + </tbody> +</table> + +<h2 id="Browser-Kompatibilität">Browser-Kompatibilität</h2> + +<div>{{ CompatibilityTable() }}</div> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari (WebKit)</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("44.0") }}</td> + <td>{{ CompatNo() }}</td> + <td>24</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>install/activate events</td> + <td>{{ CompatChrome(40.0) }}</td> + <td>{{ CompatGeckoDesktop("44.0") }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>fetch event/request/<br> + <code>respondWith()</code></td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("44.0") }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>caches/cache</td> + <td> + <p class="p1">{{CompatChrome(42.0)}}</p> + </td> + <td>{{ CompatGeckoDesktop("39.0") }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Chrome for Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>Firefox OS</th> + <th>IE Phone</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + </tr> + <tr> + <td>Basic support</td> + <td></td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td> install/activate events</td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>fetch event/request/<br> + <code>respondWith()</code></td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("44.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + <tr> + <td>caches/cache</td> + <td>{{ CompatNo() }}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoMobile("39.0") }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + <td>{{ CompatNo() }}</td> + </tr> + </tbody> +</table> +</div> + +<div class="note"> +<p><strong>Note</strong>: For backwards compatibility, you could choose to use service workers and AppCache on the same web app to do equivalent things (if the AppCache will support doing those things, of course.) What happens in such a case is that browsers that don’t support Service Workers will use AppCache, and those that do will ignore the AppCache and let the service worker take over.</p> +</div> + +<h2 id="Siehe_auch">Siehe auch</h2> + +<ul> + <li><a href="https://serviceworke.rs">ServiceWorker Cookbook</a></li> + <li><a href="/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers">Using Service Workers</a></li> + <li><a href="https://github.com/mdn/sw-test">Service workers basic code example</a></li> + <li><a href="https://jakearchibald.github.io/isserviceworkerready/">Is ServiceWorker ready?</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li> + <li><a href="/en-US/docs/Web/Guide/Performance/Using_web_workers">Using web workers</a></li> +</ul> diff --git a/files/de/web/api/service_worker_api/using_service_workers/index.html b/files/de/web/api/service_worker_api/using_service_workers/index.html new file mode 100644 index 0000000000..f45d69025f --- /dev/null +++ b/files/de/web/api/service_worker_api/using_service_workers/index.html @@ -0,0 +1,507 @@ +--- +title: Service-Worker benutzen +slug: Web/API/Service_Worker_API/Using_Service_Workers +tags: + - Anleitung + - Grundlagen +translation_of: Web/API/Service_Worker_API/Using_Service_Workers +--- +<p>{{ServiceWorkerSidebar}}</p> + +<p>{{ SeeCompatTable() }}</p> + +<div class="summary"> +<p>Dieser Artikel informiert über die ersten Schritte mit Service-Workern, inklusive der Basisarchitektur, der Registrierung eines Service-Workers, der Installation und des Aktivierungsprozesses für einen neuen Service-Worker, Aktualisierung eines Service-Workers, Cache-Kontrolle und benutzerdefinierte Antworten, alles im Kontext von einer simplen App mit Offline-Funktionalität. </p> +</div> + +<h2 id="Die_Voraussetzung_von_Service-Workern">Die Voraussetzung von Service-Workern</h2> + +<p>Ein großes Problem, unter dem Web-User seit Jahren leiden, ist der Verlust der Internetverbindung. Selbst die beste Webanwendung liefert eine furchtbare Benutzererfahrung, wenn sie nicht heruntergeladen werden kann. Es gab verschiedene Versuche, Technologien zu erstellen, die dieses Problem lösen (wie unsere <a href="/en-US/Apps/Build/Offline">Offline</a>-Seite zeigt) und einige Themen konnten so angegangen werden. Das größte Problem aber ist, dass es immer noch keinen guten allgemeinen Kontroll-Mechanismus für Asset-Caching und eigene Netzwerkanfragen gibt. <br> + <br> + Der vorausgegangene Versuch — AppCache — schien eine gute Idee, da er der Entwicklerin erlaubte, Assets zu spezifizieren, die leicht zu cachen sein sollten. Die Technologie traf jedoch Annahmen darüber, was die Entwicklerin zu tun gedachte und schlug an mehreren Stellen fehl, wenn die Webanwendung diesen Annahmen nicht eins zu eins folgte. Mehr Details dazu finden sich in Jake Archibalds "<a href="http://alistapart.com/article/application-cache-is-a-douchebag">Application Cache is a Douchebag</a>".</p> + +<div class="note"> +<p><strong>Beachte</strong>: Wird <a href="/en-US/docs/Web/HTML/Using_the_application_cache">AppCache</a> verwendet, um Offline-Support für eine Seite zur Verfügung zu stellen, wird seit Firefox 44 eine Warnmeldung auf der Console angezeigt, die Entwicklern statt dessen die Verwendung von <a href="/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Service-Workern</a> empfiehlt ({{bug("1204581")}}).</p> +</div> + +<p>Service-Worker sollen diese Probleme schließlich beheben. Ihre Syntax ist komplexer als die des AppCaches, der Vorteil ist aber, dass nun JavaScript benutzt werden kann, um die vom AppCache getroffenen Annahmen feinschrittig zu kontrollieren, was das zuvor beschriebene Problem und viele weitere löst. Mit Hilfe eines Service-Workers, kann die Webanwendung leicht aufgesetzt werden, um vorrangig gecachte Assets zu benutzen und so auch im Offline-Fall eine bestimmte Benutzererfahrung zu ermöglichen, bevor weitere Daten aus dem Netzwerk geladen werden (auch bekannt als <a href="http://offlinefirst.org/">Offline First</a>-Ansatz). Native Apps stellen dieses Feature bereits bereit, was der Grund dafür ist, dass native Apps oft Webanwendungen vorgezogen werden.</p> + +<h2 id="Einrichtung_zur_Verwendung_von_Service-Workern">Einrichtung zur Verwendung von Service-Workern</h2> + +<p>Viele der Eigenschaften von Service-Workern sind inzwischen in neueren Versionen unterstützender Browser standardmäßig eingestellt. Sollte der Beispielcode in einem der installierten Browser jedoch nicht funktionieren, kann die explizite Aktivierung einer Einstellung vonnöten sein:</p> + +<ul> + <li><strong>Firefox Nightly</strong>: Navigieren Sie zu <code>about:config</code> und setzen Sie <code>dom.serviceWorkers.enabled</code> auf true; Starten Sie den Browser anschließend neu.</li> + <li><strong>Chrome Canary</strong>: Navigieren Sie zu <code>chrome://flags</code> und aktivieren Sie <code>experimental-web-platform-features</code>; Starten Sie den Browser anschließend neu (Beachten Sie, dass einige Funktionen jetzt standardmäßig in Chrome aktiviert sind).</li> + <li><strong>Opera</strong>: Navigieren Sie zu <code>opera://flags</code> und aktivieren Sie <code>Support for ServiceWorker</code>; Starten Sie den Browser anschließend neu.</li> +</ul> + +<p>Stellen Sie weiterhin sicher, dass Ihr Code über HTTPS gesendet wird — Service-Worker sind aus Sicherheitsgründen auf HTTPS beschränkt. Da GitHub (resp. GitHub Pages) HTTPS unterstüzt, ist es ein guter Ort, um Experimente zu hosten.</p> + +<h2 id="Grundarchitektur">Grundarchitektur</h2> + +<p><img alt="" src="https://mdn.mozillademos.org/files/8241/flowchart-production-version.png" style="float: right; height: 600px; margin-left: 20px; width: 300px;"></p> + +<p>Ein einfache Implementierung von Service-Workern folgt für gewöhnlich den folgenden Schritten:</p> + +<ol> + <li>Die URL des Service-Workers wird aufgerufen und via {{domxref("serviceWorkerContainer.register()")}} registriert.</li> + <li>War dies erfolgreich, wird der Service-Worker in einem {{domxref("ServiceWorkerGlobalScope")}} ausgeführt; Im Prinzip ist dies eine besondere Art von Worker-Kontext, welcher außerhalb des Threads des Hauptscriptes läuft und keinen Zugriff auf das DOM hat.</li> + <li>Der Service-Worker kann nun Events verarbeiten.</li> + <li>Die Installation des Workers wird versucht, wenn Seiten, die durch Service-Worker verwaltet werden, wiederholt besucht werden. Ein Install-Event ist immer das erste, welches an einen Service-Worker gesendet wird (Dies kann beispielsweise benutzt werden, um den Prozess zu starten, der die IndexDB befüllt und Seiten-Assets cacht). Das ist der gleiche Ablauf, der bei der Installation einer nativen oder FirefoxOS App stattfindet - alles für die Offline-Nutzung vorbereiten.</li> + <li>Sobald der <code>oninstall</code>-Handler abgeschlossen ist, wird der Service-Worker als installiert betrachtet.</li> + <li>Der nächste Schritt ist Aktivierung. Sobald der Service-Worker installiert wurde, erhält dieser ein <code>activate</code>-Event. Der Hauptnutzen von <code>onactivate</code> ist das Aufräumen von Ressourcen, die in vorherigen Versionen des Service-Worker-Scripts genutzt wurden.</li> + <li>Der Service-Worker kann nun Webseiten verwalten, aber nur wenn diese nach dem erfolgreichen Abschluss von <code>register()</code> aufgerufen wurden. So wird ein Dokument, welches ohne Service-Worker gestartet wurde bis zu einem Neuladen nicht von einem Service-Worker verwaltet werden.</li> +</ol> + +<h3 id="Promises">Promises</h3> + +<p><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> sind ein sehr guter Mechanismus um asynchrone Operationen, deren Erfolg voneinander abhängt, auszuführen. Diese Technologie ist zentral für die Arbeitsweise von Service-Workern.<br> + <br> + Promises ermöglichen viele verschiedene Dinge, für unseren Zweck reicht es allerdings aus zu wissen, dass, wenn eine Funktion eine Promise zurück gibt, ein <code>.then()</code> an das Ende des Aufrufs gehangen werden und mit Callbacks für Erfolg, Misserfolg, etc. versehen werden kann. Des Weiteren kann <code>.catch()</code> am Ende benutzt werden, um einen besonderen Callback für den Misserfolg hinzuzufügen.</p> + +<p>Vergleichen wir nun die traditionelle synchrone Callback-Struktur mit ihrem asynchronen Promise-Äquivalent.</p> + +<h4 id="sync">sync</h4> + +<pre class="brush: js">try { + var value = myFunction(); + console.log(value); +} catch(err) { + console.log(err); +}</pre> + +<h4 id="async">async</h4> + +<pre class="brush: js">myFunction().then(function(value) { + console.log(value); + }).catch(function(err) { + console.log(err); +});</pre> + +<p>Im ersten Beispiel müssen wir warten, bis die <code>myFunction()</code> ausgeführt wird und <code>value</code> zurückgegeben wird bevor weiterer Code ausgeführt werden kann. Im zweiten Beispiel gibt <code>myFunction()</code> eine Promise für <code>value </code>zurück, danach kann weiterer Code ausgeführt werden. Wenn die Promise aufgelöst wird, wird der Code in <code>then</code> asynchron ausgeführt.<br> + <br> + Nun zu einem echten Beispiel: Was, wenn wir Bilder dynamisch laden, aber sicher stellen wollen, dass sie erst angezeigt werden, wenn sie vollständig geladen sind? Dies ist etwas, was häufig gemacht werden soll, aber sehr kompliziert werden kann. <code>.onload</code> kann benutzt werden, um ein Bild erst anzuzeigen, wenn es geladen wurde. Was aber geschieht mit Events, die stattfinden, bevor wir auf diese hören können? Man könnte versuchen, dieses Problem mit Hilfe von <code>.complete</code> zu umgehen, was jedoch nicht betriebssicher ist und den Umgang mit mehreren Bildern gleichzeitig nicht abdeckt. Auch ist dieser Ansatz weiterhin synchron und blockiert somit den Haupt-Thread.<br> + <br> + Wir können jedoch stattdessen eine eigene Promise bauen, die diesen Fall abdeckt. (Siehe unser <a href="https://github.com/mdn/promises-test">Promises-Test-Beispiel</a> für den entsprechenden Quellcode, oder <a href="https://mdn.github.io/promises-test/"> hier </a> für eine Liveanwendung.)</p> + +<p>{{note("Eine echte Service-Worker-Implementierung würde Caching und das <code>onfetch</code>-Event statt der überholten XMLHttpRequest API benutzen. Diese Features wurden hier nicht benutzt, um den Fokus auf Promises setzen zu können.")}}</p> + +<pre class="brush: js">function imgLoad(url) { + return new Promise(function(resolve, reject) { + var request = new XMLHttpRequest(); + request.open('GET', url); + request.responseType = 'blob'; + + request.onload = function() { + if (request.status == 200) { + resolve(request.response); + } else { + reject(Error('Bild wurde nicht geladen; Fehlercode:' + request.statusText)); + } + }; + + request.onerror = function() { + reject(Error('Ein Netzwerkfehler ist aufgetreten.')); + }; + + request.send(); + }); +}</pre> + +<p>Wir geben eine neue Promise mit Hilfe des <code>Promise()</code>-Konstruktors zurück, der als Parameter eine Callback-Funktion mit <code>resolve</code>- und <code>reject</code>- Parametern erhält. Innerhalb dieser Funktion müssen wir definieren, was passieren muss, dass die Promise erfolgreich aufgelöst oder zurückgewiesen wird — In diesem Fall also entweder einen <code>200 OK</code> Status zurückgegeben bekommen oder nicht — und dann bei Erfolg <code>resolve</code> und bei Misserfolg <code>reject</code> aufzurufen. Der restliche Inhalt dieser Funktion sind Schritte zum Umgang mit XHR, über die wir uns im Moment keine Gedanken machen müssen.</p> + +<p>Wenn wir beim Aufruf der <code>imgLoad()</code>-Funktion angelangt sind, rufen wir diese mit der URL zu dem Bild, welches wir laden möchten, wie erwartet auf, der restliche Code ist jedoch ein bisschen anders:</p> + +<pre class="brush: js">var body = document.querySelector('body'); +var myImage = new Image(); + +imgLoad('myLittleVader.jpg').then(function(response) { + var imageURL = window.URL.createObjectURL(response); + myImage.src = imageURL; + body.appendChild(myImage); +}, function(Error) { + console.log(Error); +});</pre> + +<p>Die <code>then()</code>-Methode wird an das Ende des Funktionsaufrufes gehangen, und enthält zwei Funktionen – die erste wird ausgeführt, wenn die Promise aufgelöst wird, die zweite, wenn sie zurückgewiesen wird. Im Auflösungsfall zeigen wir das Bild innerhalb von <code>myImage</code> an und fügen es an den body an (Das Argument der <code>then()</code>-Funktion ist die request.response innerhalb der resolve-Methode des Promises); im Zurückweisungsfall geben wir einen Fehler in der Konsole aus.</p> + +<p>Dies alles geschieht asynchron.</p> + +<div class="note"> +<p><strong>Beachte</strong>: Promises können aneinandergekettet werden, zum Beispiel:<br> + <code>myPromise().then(success, failure).then(success).catch(failure);</code></p> +</div> + +<div class="note"> +<p><strong>Beachte</strong>: Weitere Informationen zu Promises finden sich in Jake Archibalds Artikel <a href="http://www.html5rocks.com/en/tutorials/es6/promises/">JavaScript Promises: there and back again</a>.</p> +</div> + +<h2 id="Service-Worker_Demo">Service-Worker Demo</h2> + +<p>Um die Grundlagen der Registrierung und Installation eines Service-Workers zu demonstrieren, haben wir eine kleine Demo namens <a href="https://github.com/mdn/sw-test">sw-test</a> geschaffen, die eine überschaubare Star-Wars-Lego-Bildergalerie ist. Sie nutzt eine Promise-unterstützte Funktion um Bilddaten von einem JSON-Objekt zu lesen und diese Bilder mit Hilfe von Ajax zu laden, bevor sie untereinander auf der Webseite angezeigt werden. Die Anwendung ist statisch und einfach gehalten. Sie registriert, installiert und aktiviert des Weiteren einen Service-Worker und cacht die erforderlichen Dateien für die Offline-Nutzung, sollten weitere Aspekte dieser Spezifikation bei den aufrufenden Browsern aktiviert sein.</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/8243/demo-screenshot.png" style="display: block; height: 410px; margin: 0px auto; width: 480px;"><br> + <br> + <br> + Der <a href="https://github.com/mdn/sw-test/">Quellcode</a> ist auf GithHub zu finden, eine Live-Version kann <a href="https://mdn.github.io/sw-test/">hier</a> betrachtet werden. Der Aspekt, den wir hier betrachten wollen, ist die Promise (siehe <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L17-L42">app.js Zeilen 17-42</a>), die eine modifizierte Version dessen ist, was in der <a href="https://github.com/mdn/promises-test">Promises Test Demo</a> zu lesen war. Die Unterschiede sind die folgenden:</p> + +<ol> + <li>Im Original haben wir nur eine URL zu einem Bild, welches wir laden wollen, hineingereicht. In dieser Version reichen wir ein JSON-Fragment hinein, welches alle Daten für ein einzelnes Bild beinhaltet (in <a href="https://github.com/mdn/sw-test/blob/gh-pages/image-list.js">image-list.js</a> kann ein Blick darauf geworfen werden). Das geschieht, weil wegen der Asynchronität die gesamten Daten für jedes Auflösen der Promise in in diese hineingereicht werden müssen. Würde nur die URL hineingereicht werden und versucht werden, während der <code>for()-</code>Schleife, auf andere Dinge im JSON zuzugreifen, schlüge dies fehl, da die Promise nicht zeitgleich mit dem Abschluss der Iteration auflösen würde, da dies ein synchroner Prozess ist.</li> + <li>Wir lösen die Promise des Weiteren mit einem Array auf, da wir den geladenen Bild-Blob, aber auch Bildname, Bildnachweis und Alt-Text später der auflösenden Funktion zur Verfügung stellen wollen. (siehe <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L26-L29">app.js Zeilen 26-29</a>). Promises lösen mit einem einzigen Argument auf, werden also mehrere Werte gebraucht, muss dies über ein Array bzw. Objekt gelöst werden.</li> + <li>Um auf die Werte der aufgelösten Promise zuzugreifen, greifen wir auf eben jene Funktion zu. (siehe <a href="https://github.com/mdn/sw-test/blob/gh-pages/app.js#L55-L59">app.js Zeilen 55-59</a>.) Dies mag auf den ersten Blick seltsam erscheinen, ist aber die korrekte Handhabung von Promises.</li> +</ol> + +<h2 id="Service-Worker_auf_die_Bühne_bitte!">Service-Worker auf die Bühne, bitte!</h2> + +<p>Auf geht's zu Service-Workern!</p> + +<h3 id="Service-Worker_registrieren">Service-Worker registrieren</h3> + +<p>Der erste Code-Block in der JavaScript-Datei unserer App — <code>app.js</code> — sieht wie folgt aus. Dies ist unser Startpunkt für die Nutzung von Service-Workern.</p> + +<pre class="brush: js">if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) { + // Registrierung erfolgreich + console.log('Registrierung erfolgreich. Scope ist ' + reg.scope); + }).catch(function(error) { + // Registrierung fehlgeschlagen + console.log('Registrierung fehlgeschlagen mit ' + error); + }); +};</pre> + +<ol> + <li>Der äußere Block kümmert sich um die Feature-Erkennung um sicher zu stellen, dass Service-Worker unterstützt werden, bevor einer registriert wird.</li> + <li>Als nächstes nutzen wir die {{domxref("ServiceWorkerContainer.register()") }}-Funktion, um den Service Worker für diese Seite zu registrieren. Dieser ist nur eine JavaScript-Datei innerhalb unserer App. (Beachte, dass die URL der Datei relativ zum Ursprung ist, und nicht zur JavaScript-Datei, die sie referenziert.)</li> + <li>Der <code>scope</code> Parameter ist optional, und kann benutzt werden, um den Teil der. Applikation einzuschränken, den der Service-Worker kontrollieren soll. In diesem Fall, haben wir '<code>/sw-test/' </code>spezifiziert, was alle Inhalte unter dem App-Ursprungsverzeichnis beinhaltet. Wird der Parameter weggelassen, würde standardmäßig dieses Verzeichnis gewählt werden. Wir haben es hier zu Anschauungszwecken jedoch spezifiziert..</li> + <li>Die <code>.then()</code> Promise-Funktion wird genutzt, um den Erfolgsfall an unsere Promise-Struktur zu hängen. Wenn die Promise erfolgreich auflöst, wird der Programmcode innerhalb dieser Funktion ausgeführt.</li> + <li>Zuletzt fügen wir die<code>.catch()</code>-Funktion ans Ende an, die ausgeführt wird, sollte die Promise zurückgewiesen werden.</li> +</ol> + +<p>Jetzt ist ein Service-Worker registriert, der in einem Worker-Kontext läuft und daher keinen Zugriff auf das DOM hat. Code im Service-Worker wird außerhalb der normalen Seiten ausgeführt, um deren Laden zu kontrollieren.<br> + <br> + Ein einzelner Service-Worker kann viele Seiten kontrollieren. Wird eine Seite innerhalb des spezifizierten Codes geladen, wird der Service-Worker für diese eingerichtet und nimmt seine Arbeit auf. Es sollte daher nicht vergessen werden, dass vorsichtig mit globalen Variablen im Service-Worker-Script umgegangen werden muss: Jede Seite im Scope hat den selben Worker, mit dem sie arbeitet.</p> + +<div class="note"> +<p><strong>Beachte</strong>: Service-Worker funktionieren wie Proxy-Server, was es unter anderem erlaubt, Netzwerkanfragen und -antworten zu modifizieren und mit Objekten aus dem Cache zu ersetzen.</p> +</div> + +<div class="note"> +<p><strong>Beachte</strong>: Eine gute Sache an Service-Workern ist, dass, wenn Feature-Erkennung wie im obigen Beispiel verwendet wird, Browser, die Service-Worker nicht unterstützen, die App dennoch normal und wie erwartet benutzen können. Wenn weiterhin AppCache und Service-Worker gemeinsam auf einer Seite benutzt werden, werden Browser, die AppCache aber nicht Service-Worker unterstützen, ersteres benutzen. Browser die beides unterstützen, werden AppCache zu Gunsten von Service-Workern ignorieren.</p> +</div> + +<h4 id="Wieso_schlägt_die_Registrierung_meines_Service_Workers_fehl">Wieso schlägt die Registrierung meines Service Workers fehl?</h4> + +<p>Dies kann aus folgenden Gründen passieren:</p> + +<ol> + <li>Die Applikation läuft nicht unter HTTPS</li> + <li>Der Pfad zur Service-Worker-Datei ist nicht korrekt beschrieben — er muss relativ zum Ursprung geschrieben sein, nicht relativ zum Wurzelverzeichnis der App. In unserem Beispiel liegt der Worker unter <code>https://mdn.github.io/sw-test/sw.js</code>, wohingegen das Wurzelverzeichnis der App <code>https://mdn.github.io/sw-test/ </code>ist. Der Pfad muss also als <code>/sw-test/sw.js</code>, geschrieben werden, und nicht als <code>/sw.js</code>.</li> + <li>Der Service-Worker, auf den verwiesen wird, hat einen anderen Ursprung als die App. Auch das ist nicht gestattet.</li> +</ol> + +<p>Weiterhin zu beachten:</p> + +<ul> + <li>Der Service-Worker wird nur Netzwerkanfragen von Clients innerhalb seines Scopes abgreifen.</li> + <li>Der maximale Scope eines Service-Workers ist der Ort des Workers selbst.</li> + <li>Ist der Service-Worker auf einem Client aktiv, der mit dem <code>Service-Worker-Allowed-</code>Header ausgeliefert wird, kann eine Liste von maximalen Scopes für diesen Worker definiert werden.</li> + <li>Im Firefox sind Service-Worker-APIs versteckt und können nicht genutzt werden, wenn die Nutzer sich im Inkognito-Modus befinden.</li> +</ul> + +<h3 id="Installation_und_Aktivierung_Füllen_des_Caches">Installation und Aktivierung: Füllen des Caches</h3> + +<p>Nachdem der Service-Worker registriert wurde, versucht der Browser den Service-Worker für die entsprechende Seite zu installieren und zu aktivieren.<br> + <br> + Das Install-Event wird abgesetzt wenn eine Installation erfolgreich abgeschlossen wurde. Dieses Event wird für gewöhnlich dazu genutzt, den Offline-Cache des Browsers mit den Assets zu befüllen, die benötigt werden, damit die App offline laufen kann. Um das zu erreichen, nutzen wir die Storage-API der Service-Worker — {{domxref("cache")}} — ein globales Objekt des Service-Workers, welches uns erlaubt Assets, die durch Netzwerkantworten geliefert wurden, zu speichern. Diese API arbeitet ähnlich wie der Browser interne Cache, ist allerdings Domain spezifisch und besteht, bis Gegenteiliges gesagt wird, was erneut volle Kontrolle erlaubt.</p> + +<div class="note"> +<p><strong>Beachte</strong>: Die Cache API ist nicht in jedem Browser unterstützt. (Siehe auch den Abschnitt {{anch("Browserkompabilität")}} für weitere Informationen.) Soll sie jetzt genutzt werden, kann ein Polyfill in Betracht gezogen werden, wie er beispielsweise in <a href="https://github.com/Polymer/topeka/blob/master/sw.js">Googles Topeka Demo</a> enthalten ist, oder die Assets in <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> gespeichert werden.</p> +</div> + +<p>Wir beginnen diesen Abschnitt mit einem Blick auf ein Code-Beispiel — Das ist der <a href="https://github.com/mdn/sw-test/blob/gh-pages/sw.js#L1-L18">erste Block in unserem Service-Worker</a>:</p> + +<pre class="brush: js">this.addEventListener('install', function(event) { + event.waitUntil( + caches.open('v1').then(function(cache) { + return cache.addAll([ + '/sw-test/', + '/sw-test/index.html', + '/sw-test/style.css', + '/sw-test/app.js', + '/sw-test/image-list.js', + '/sw-test/star-wars-logo.jpg', + '/sw-test/gallery/', + '/sw-test/gallery/bountyHunters.jpg', + '/sw-test/gallery/myLittleVader.jpg', + '/sw-test/gallery/snowTroopers.jpg' + ]); + }) + ); +});</pre> + +<ol> + <li>Hier wird dem Service-Worker (mit Hilfe von <code>this</code>) ein <code>install</code>Event-Listener hinzugefügt, und die {{domxref("ExtendableEvent.waitUntil()") }}-Methode dem Event angehängt — das stellt sicher, dass der Service-Worker nicht installiert wird, bis der Code innerhalb von <code>waitUntil()</code> erfolgreich ausgeführt wurde.</li> + <li>Innerhalb von <code>waitUntil()</code> benutzen wir die <a href="/en-US/docs/Web/API/CacheStorage/open"><code>caches.open()</code></a>-Methode um einen neuen Cache mit dem Namen <code>v1</code> zu erstellen, welcher die erste Version des Ressourcenspeichers unserer Seite sein wird. Das gibt eine Promise für den erstellten Cache zurück; sobald diese aufgelöst ist, rufen wir eine Funktion auf, die wiederum <code>addAll()</code> auf dem erstellten Cache aufruft und als Parameter einen Array mit zum Ursprung relativen URLs der zu cachenden Ressourcen enthält.</li> + <li>Sollte die Promise zurückgewiesen werden, schlägt die Installation fehl und der Worker tut nichts. Das ist in Ordnung und bietet die Möglichkeit, den Code zu korrigieren und es bei der nächsten Registrierung erneut zu versuchen.</li> + <li>Nach erfolgreicher Installation wird der Service-Worker aktiviert. Wurde der Service-Worker zum ersten mal installiert und aktiviert ist dies nicht weiter von Bedeutung, aber wichtig, wenn der Service-Worker aktiviert wird. (Siehe den Abschnitt {{anch("Aktualisieren des Service-Workers") }} im späteren Verlauf.)</li> +</ol> + +<div class="note"> +<p><strong>Beachte</strong>: <a href="/en-US/docs/Web/Guide/API/DOM/Storage">localStorage</a> funktioniert ähnlich wie der Service-Worker-Cache, ist aber synchron und daher in Service-Workern nicht gestattet.</p> +</div> + +<div class="note"> +<p><strong>Beachte</strong>: <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> kann, wenn nötig, innerhalb eines Service-Workers benutzt werden.</p> +</div> + +<h3 id="Eigene_Antworten_auf_Netzwerkanfragen">Eigene Antworten auf Netzwerkanfragen</h3> + +<p>Da jetzt die Assets der Seite gecacht wurden, kann nun den Service-Workern mitgeteilt werden, was sie mit dem gecachten Inhalt tun sollen. Dies geschieht mit Hilfe des <code>fetch</code>-Events.</p> + +<p>Ein <code>fetch</code>-Event wird jedes Mal abgesetzt, wenn eine Ressource, die vom Service-Worker kontrolliert wird (also die Dokumente innerhalb seines Scopes sowie die dort referenzierten Ressourcen), geladen wird. (Wenn also <code>index.html</code> eine Cross-Origin-Anfrage an ein eingebettetes Bild absetzt, geht auch das durch den Service-Worker.)</p> + +<p>Ein <code>fetch</code> Event-Listener kann dem Service-Worker hinzugefügt, und die <code>respondWith()</code>-Methode des Events aufgerufen werden, um die HTTP-Antworten abzufangen und mit unseren eigenen zu ersetzen.</p> + +<pre class="brush: js">this.addEventListener('fetch', function(event) { + event.respondWith( + // Hier eigene Antworten + ); +});</pre> + +<p>Es kann damit begonnen werden, jedes Mal mit der Ressource zu antworten, deren URL der der Netzwerkanfrage entspricht:</p> + +<pre class="brush: js">this.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request) + ); +});</pre> + +<p><code>caches.match(event.request)</code> erlaubt uns, jede Ressource, die vom Netzwerk angefragt wird, mit der entsprechenden Ressource des Caches zu ersetzen, so diese verfügbar ist. Der Abgleich geschieht wie bei normalen HTTP-Anfragen über URLs und Header.</p> + +<p>Betrachten wir einige weitere Optionen, die wir haben, wenn wir unsere eigenen Antworten definieren wollen (siehe auch die <a href="/en-US/docs/Web/API/Fetch_API">Fetch-API Dokumentation</a> für weitere Informationen über {{domxref("Request")}}- und {{domxref("Response")}}-Objekte.)</p> + +<ol> + <li> + <p>Der <code>{{domxref("Response.Response","Response()")}}</code>-Konstruktor erlaubt uns das Erstellen einer benutzerdefinierten Antwort. In diesem Fall geben wir eine einfache Zeichenkette zurück:</p> + + <pre class="brush: js">new Response('Hallo vom freundlichen Service-Worker!');</pre> + </li> + <li> + <p>Das folgende komplexere <code>Response</code>-Beispiel zeigt, dass optional eine Anzahl an Headern in der Antwort gesetzt werden können, die die Standard HTTP-Responce-Header emulieren. Hier wird dem Browser nut der Content-Type unserer künstlichen Antwort mitgeteilt:</p> + + <pre class="brush: js">new Response('<p>Hallo from freundlichen Service-Worker!</p>', { + headers: { 'Content-Type': 'text/html' } +})</pre> + </li> + <li> + <p>Wenn im Cache kein Treffer gefunden wurde, könnte der Browser angewiesen werden, die standardmäßigen Netzwerkanfrage mit {{domxref("GlobalFetch.fetch","fetch")}} auszuführen, um die Ressource aus dem Netzwerk zu laden, wenn dieses verfügbar ist:</p> + + <pre class="brush: js">fetch(event.request)</pre> + </li> + <li> + <p>Wenn es keinen Treffer gab und auch das Netzwerk nicht verfügbar ist, kann die Anfrage mit einer Ausweichseite beantwortet werden, indem {{domxref("CacheStorage.match","match()")}} folgendermaßen verwendet wird:</p> + + <pre class="brush: js">caches.match('/fallback.html');</pre> + </li> + <li> + <p>Viele Informationen über jeden Request können erhalten werden, indem Parameter des {{domxref("Request")}}-Objekts, welches vom {{domxref("FetchEvent")}} zurückgegeben wird, aufgerufen werden.</p> + + <pre class="brush: js">event.request.url +event.request.method +event.request.headers +event.request.body</pre> + </li> +</ol> + +<h2 id="Umgang_mit_fehlgeschlagenen_Cache-Anfragen">Umgang mit fehlgeschlagenen Cache-Anfragen</h2> + +<p><code>caches.match(event.request)</code>ist hilfreich, wenn es einen Treffer im Service-Worker-Cache gibt. Was aber, wenn das nicht der Fall ist? Stellten wir keine Möglichkeit zur Behandlung einer scheiternden Cache-Anfrage bereit, würde unsere Promise zurückgewiesen werden und wir würden einem Netzwerkfehler gegenüber stehen, wenn es keinen Treffer im Cache gibt.</p> + +<p>Glücklicherweise erlaubt die Promise basierte Struktur des Service-Workers weitere Optionen bereitzustellen. Beispielsweise:</p> + +<pre class="brush: js">this.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request).catch(function() { + return fetch(event.request); + }) + ); +});</pre> + +<p>Falls die Promise zurückgewiesen wird, gibt die <code>catch()</code>-Funktion die Standard-Netzwerkanfrage für die Ressource zurück, was es erlaubt, diese vom Server zu laden, sollte eine Netzwerkverbindung bestehen.</p> + +<p>Ein weiterer Weg wäre, die Ressource nicht bloß aus dem Netzwerk zu laden, sondern für zukünftige Anfragen im Cache zu speichern, damit diese ebenfalls offline verwendet werden können. Das bedeutet, sollten weitere Bilder zur Star-Wars-Galerie hinzugefügt werden, kann unsere App diese laden und cachen. Es würde folgendermaßen implementiert werden:</p> + +<pre class="brush: js">this.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request).catch(function() { + return fetch(event.request).then(function(response) { + return caches.open('v1').then(function(cache) { + cache.put(event.request, response.clone()); + return response; + }); + }); + }) + ); +});</pre> + +<p>Wir geben an dieser Stelle die Standard-Netzwerkanfrage mit <code>return</code><code> fetch(event.request) </code>zurück, die wiederum eine Promise zurückgibt. Wenn diese Promise aufgelöst wird, reagieren wir mit einer Funktion, die unseren Cache mit <code>caches.open('v1')</code> öffnet, was eine weitere Promise zurückgibt. Wenn diese auflöst, wird <code>cache.put()</code> benutzt, um die Ressource dem Cache hinzuzufügen. Diese wird aus <code>event.request </code>gezogen, die Netzwerkantwort mit <code>response.clone()</code> kopiert und dem Cache hinzugefügt. Die Kopie wird gespeichert und die Original-Netzwerkantwort an den Browser und somit an die Seite, die die Anfrage gestellt hat, zurückgegeben.</p> + +<p>Warum das alles? Der Datenstrom von Netzwerkanfrage und -antwort kann nur ein einziges Mal gelesen werden. Damit die Netzwerkantwort an den Browser zurückgegeben werden und trotzdem auch im Cache gespeichert werden kann, muss sie kopiert werden. Dadurch kann das Original an den Browser gereicht und die Kopie im Cache gespeichert werden. Original und Kopie werden somit jeweils nur einmal gelesen.</p> + +<p>Zuletzt bleibt noch das Problem, was passieren soll, wenn es weder einen Treffer im Cache noch eine verfügbare Netzwerkverbindung gibt. Zur Zeit schlägt unsere Anfrage an die Ressource fehl. Wenn wir eine Ausweichlösung bereitstellen, werden, egal was passiert, die Nutzer eine Antwort bekommen:</p> + +<pre><code>this.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request).then(function(resp) { + return resp || fetch(event.request).then(function(response) { + caches.open('v1').then(function(cache) { + cache.put(event.request, response.clone()); + }); + return response; + }); + }).catch(function() { + return caches.match('/sw-test/gallery/myLittleVader.jpg'); + }) + ); +});</code></pre> + +<p>Wir haben uns an dieser Stelle für ein Ausweichbild entschieden, da die einzigen Anfragen, die potentiell fehlschlagen, die für neue Bilder in der Galerie sein werden. Um alles andere wurde sich bereits während des <code>install</code>-Events der Installation gekümmert.</p> + +<h2 id="Aktualisieren_des_Service-Workers"><font color="#83d0f2">Aktualisieren des Service-Workers</font></h2> + +<p>Wurde der Service-Worker bereits installiert, aber eine neue Version bei Neuladen der Seite bereitgestellt werden, wird dieser neue Worker im Hintergrund installiert, jedoch noch nicht aktiviert. Das geschieht erst dann, wenn keine der Seiten, die den alten Worker nutzen, mehr geladen werden. Sobald dies der Fall ist, wird der neue Service-Worker aktiviert.</p> + +<p>Damit dies geschehen kann, muss der<code>install</code>-Event-Listener im neuen Service-Worker ungefähr folgendermaßen angepasst werden (Augenmerk auf die neue Versionsnummer):</p> + +<pre class="brush: js">this.addEventListener('install', function(event) { + event.waitUntil( + caches.open('v2').then(function(cache) { + return cache.addAll([ + '/sw-test/', + '/sw-test/index.html', + '/sw-test/style.css', + '/sw-test/app.js', + '/sw-test/image-list.js', + + … + + // hier können weitere neue Ressourcen stehen... + ]); + }); + ); +});</pre> + +<p>Während dies alles geschieht, ist weiterhin die vorherige Version des Service-Workers für die Verwaltung der Anfragen verantwortlich und die neue Version wird im Hintergrund installiert. Der neue Cache hat in diesem Beispiel den Namen <code>v2</code>, so dass der Cache mit dem Namen <code>v1</code> nicht beeinträchtigt wird.</p> + +<p>Wenn keine Seiten mehr die aktuelle Version benutzen, wird der neue Worker aktiviert und verwaltet die Netzwerkanfragen.</p> + +<h3 id="Löschen_des_alten_Caches">Löschen des alten Caches</h3> + +<p>Es steht weiterhin ein <code>activate</code>-Event zur Verfügung. Es wird generell genutzt, um Dinge abzuhandeln, die die alte Version stark beeinträchtigt hätten, würden sie zu deren Laufzeit ausgeführt werden. Dies betrifft zum Beispiel das Löschen der alten Caches. Das Event ist auch nützlich, um Daten zu löschen, die nicht mehr gebraucht werden und so zu verhindern, dass zu viel Speicherplatz verbraucht wird — jeder Browser hat eine feste Obergrenze für die Menge an Cache-Speicherplatz, die ein Service-Worker nutzen kann. Der Browser selbst verwaltet den Speicherplatz an sich selbstständig, aber es kann passieren, dass der Cache-Speicherplatz für eine Seite gelöscht wird, denn wenn der Browser Daten dieser Art löscht, dann entweder alle oder keine Daten der Seite.</p> + +<p>Promises, die <code>waitUntil()</code> hineingereicht werden, blockieren weitere Events bis diese abgearbeitet wurden, so dass sicher gestellt werden kann, dass alle Aufräumarbeiten abgeschlossen sind, bevor die erste Netzwerkanfrage an den neuen Cache gestellt wird.</p> + +<pre class="brush: js">this.addEventListener('activate', function(event) { + var cacheWhitelist = ['v2']; + + event.waitUntil( + caches.keys().then(function(keyList) { + return Promise.all(keyList.map(function(key) { + if (cacheWhitelist.indexOf(key) === -1) { + return caches.delete(key); + } + })); + }) + ); +});</pre> + +<h2 id="Entwicklertools">Entwicklertools</h2> + +<p>Chrome hat <code>chrome://inspect/#service-workers</code>, wo Aktivität und Speicherplatz der aktuellen Service-Worker eingesehen werden können, und <code>chrome://serviceworker-internals</code>, wo mehr Details zu finden sind und Service-Worker gestartet, beendet und der Fehlersuche unterworfen werden können. Zukünftige Versionen werden zusätzlich Mittel zur Netzwerkdrosselung und einen Offline-Modus bereitsellen, um schlechte oder nicht vorhandene Netzwerkverbindungen zu simulieren.</p> + +<p>Auch Firefox hat die Entwicklung nützlicher Werkzeuge in Bezug auf Service-Worker begonnen:</p> + +<ul> + <li>Unter <a>about:serviceworkers</a> können registrierte Service-Worker eingesehen und aktualisiert oder entfernt werden</li> + <li>Für Testzwecke kann die HTTPS-Beschränkung durch Aktivieren der "Service-Worker über HTTP aktivieren (bei geöffneten Entwicklerwerkzeugen)"-Option in den Firefox-Entwicklerwerkzeug-Optionen (Zahnrad-Menü)</li> +</ul> + +<h2 id="Spezifikationen">Spezifikationen</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Spezifikation</th> + <th scope="col">Status</th> + <th scope="col">Kommentar</th> + </tr> + <tr> + <td>{{SpecName('Service Workers', '')}}</td> + <td>{{Spec2('Service Workers')}}</td> + <td>Initiale Definition.</td> + </tr> + </tbody> +</table> + +<h2 id="Browserkompatibilität">Browserkompatibilität</h2> + +<div>{{CompatibilityTable}}</div> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Opera</th> + <th>Safari (WebKit)</th> + </tr> + <tr> + <td>Grundlegende Unterstüzung</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatGeckoDesktop("33.0") }}</td> + <td>{{CompatNo}}</td> + <td>24</td> + <td>{{CompatNo}}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Chrome for Android</th> + <th>Firefox Mobile (Gecko)</th> + <th>Firefox OS</th> + <th>IE Phone</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + </tr> + <tr> + <td>Grundlegende Unterstüzung</td> + <td>{{CompatNo}}</td> + <td>{{CompatChrome(40.0)}}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{ CompatVersionUnknown }}</td> + <td>{{CompatNo}}</td> + <td>{{ CompatVersionUnknown() }}</td> + <td>{{CompatNo}}</td> + </tr> + </tbody> +</table> +</div> + +<h2 id="Siehe_auch">Siehe auch</h2> + +<ul> + <li><a href="https://jakearchibald.github.io/isserviceworkerready/">Is ServiceWorker ready?</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li> + <li><a href="/en-US/docs/Web/Guide/Performance/Using_web_workers">Using web workers</a></li> +</ul> |
