ES-Module in Service Workern

Eine moderne Alternative zu importScripts().

Hintergrund

ES-Module sind bei Entwicklern schon länger beliebt. Zusätzlich zu einer Reihe weiterer Vorteile bieten sie ein universelles Modulformat, bei dem freigegebener Code einmal freigegeben und in Browsern und in alternativen Laufzeiten wie Node.js ausgeführt werden kann. Alle modernen Browser unterstützen zwar in gewissem Umfang ES-Module, aber nicht überall, wo Code ausgeführt werden kann. Insbesondere die Unterstützung für den Import von ES-Modulen in den Service Worker eines Browsers wird gerade zunehmend unterstützt.

In diesem Artikel wird der aktuelle Status der Unterstützung von ES-Modulen in Service Workern in gängigen Browsern beschrieben. Außerdem werden einige Fehler beschrieben, die Sie vermeiden sollten, und Best Practices für den Versand von abwärtskompatiblem Service Worker-Code.

Anwendungsfälle

Der ideale Anwendungsfall für ES-Module innerhalb von Service Workern ist das Laden einer modernen Bibliothek oder eines Konfigurationscodes, der mit anderen Laufzeiten gemeinsam genutzt wird, die ES-Module unterstützen.

Der Versuch, Code auf diese Weise vor ES-Modulen freizugeben, erforderte die Verwendung älterer "universeller" Modulformate wie UMD, die nicht benötigten Textbausteine enthielten, und das Schreiben von Code, durch den Änderungen an global bereitgestellten Variablen vorgenommen wurden.

Über ES-Module importierte Skripts können den Update-Ablauf des Service Workers auslösen, wenn sich ihr Inhalt entsprechend dem Verhalten von importScripts() ändert.

Aktuelle Beschränkungen

Nur statische Importe

ES-Module können auf zwei Arten importiert werden: entweder statisch mithilfe der import ... from '...'-Syntax oder dynamisch mit der Methode import(). Innerhalb eines Service Workers wird derzeit nur die statische Syntax unterstützt.

Diese Einschränkung entspricht einer ähnlichen Einschränkung für die importScripts()-Nutzung. Dynamische Aufrufe von importScripts() funktionieren nicht innerhalb eines Service-Workers. Alle importScripts()-Aufrufe, die grundsätzlich synchron sind, müssen abgeschlossen sein, bevor der Service-Worker die install-Phase abschließt. Durch diese Einschränkung wird sichergestellt, dass der Browser den gesamten JavaScript-Code, der für die Implementierung eines Service Workers während der Installation benötigt wird, kennt und implizit zwischenspeichern kann.

Letztendlich wird diese Einschränkung möglicherweise aufgehoben und dynamische ES-Modulimporte können zulässig sein. Achten Sie vorerst darauf, dass Sie die statische Syntax nur innerhalb eines Service Workers verwenden.

Was ist mit anderen Arbeitern?

Die Unterstützung von ES-Modulen in „dedizierten“ Workern, die mit new Worker('...', {type: 'module'}) erstellt wurden, ist weiter verbreitet und wird seit Version 80 sowie in aktuellen Versionen von Safari in Chrome und Edge unterstützt. Sowohl statische als auch dynamische ES-Modulimporte werden in dedizierten Workern unterstützt.

Chrome und Edge unterstützen ES-Module in gemeinsam genutzten Workern seit Version 83. Derzeit wird dies jedoch von keinem anderen Browser unterstützt.

Keine Unterstützung für das Importieren von Karten

Mithilfe von Importzuordnungen können Laufzeitumgebungen Modulspezifizierer neu schreiben, um beispielsweise die URL eines bevorzugten CDN voranzustellen, aus dem die ES-Module geladen werden können.

Chrome und Edge ab Version 89 unterstützen zwar Importkarten, können jedoch derzeit nicht mit Service Workern verwendet werden.

Unterstützte Browser

ES-Module in Service Workern werden in Chrome und Edge ab Version 91 unterstützt.

Safari wird in der Version 122 der Technologievorschau unterstützt. Entwickler sollten daher rechnen, dass diese Funktion in Zukunft in der stabilen Version von Safari veröffentlicht wird.

Beispielcode

Dies ist ein einfaches Beispiel für die Verwendung eines freigegebenen ES-Moduls im window-Kontext einer Webanwendung und die Registrierung eines Service Workers, der dasselbe ES-Modul verwendet:

// Inside config.js:
export const cacheName = 'my-cache';
// Inside your web app:
<script type="module">
  import {cacheName} from './config.js';
  // Do something with cacheName.

  await navigator.serviceWorker.register('es-module-sw.js', {
    type: 'module',
  });
</script>
// Inside es-module-sw.js:
import {cacheName} from './config.js';

self.addEventListener('install', (event) => {
  event.waitUntil((async () => {
    const cache = await caches.open(cacheName);
    // ...
  })());
});

Abwärtskompatibilität

Das obige Beispiel würde gut funktionieren, wenn alle Browser ES-Module in Service-Workern unterstützen. Zum Zeitpunkt der Veröffentlichung dieses Artikels ist dies jedoch nicht der Fall.

Um auch Browser ohne integrierte Unterstützung zu berücksichtigen, können Sie Ihr Service Worker-Skript über einen ES-Modul-kompatiblen Bundler ausführen, um einen Service Worker zu erstellen, der den gesamten Modulcode inline enthält und in älteren Browsern funktioniert. Wenn die zu importierenden Module bereits im IIFE- oder UMD-Format verfügbar sind, können Sie sie mit importScripts() importieren.

Sobald zwei Versionen Ihres Service Workers verfügbar sind – eine mit ES-Modulen und die andere nicht – müssen Sie ermitteln, was der aktuelle Browser unterstützt, und das entsprechende Service Worker-Skript registrieren. Die Best Practices für die Erkennung der Unterstützung sind noch in der Entwicklung. Empfehlungen finden Sie in der Diskussion in diesem GitHub-Problem.

_Foto von Vlado Paunovic auf Unsplash_