Modules ES dans les service workers

Alternative moderne à importScripts().

Contexte

Les modules ES sont appréciés des développeurs depuis un certain temps. En plus de nombreux autres avantages, elles offrent la promesse d'un format de module universel dans lequel le code partagé peut être publié une fois et exécuté dans les navigateurs et dans d'autres environnements d'exécution tels que Node.js. Bien que tous les navigateurs récents soient compatibles avec certains modules ES, ils ne sont pas tous compatibles partout où le code peut être exécuté. Plus précisément, l'importation de modules ES dans le service worker d'un navigateur commence tout juste à se généraliser.

Cet article décrit l'état actuel de la compatibilité du module ES avec les service workers sur les navigateurs courants, ainsi que certains pièges à éviter et les bonnes pratiques pour envoyer du code de service worker rétrocompatible.

Cas d'utilisation

Le cas d'utilisation idéal des modules ES dans les service workers consiste à charger une bibliothèque ou un code de configuration moderne partagé avec d'autres environnements d'exécution compatibles avec les modules ES.

Tentative de partage de code de cette manière avant les modules ES impliquant d'utiliser d'anciens formats de module "universels" tels que UMD, qui incluent du code récurrent inutile, et l'écriture de code qui apporte des modifications aux variables exposées de manière globale.

Les scripts importés via des modules ES peuvent déclencher le flux de mise à jour du service worker si leur contenu change, ce qui correspond au comportement de importScripts().

Limites actuelles

Importations statiques uniquement

Les modules ES peuvent être importés de deux manières différentes: en mode statique, à l'aide de la syntaxe import ... from '...', ou de manière dynamique, à l'aide de la méthode import(). Dans un service worker, seule la syntaxe statique est actuellement acceptée.

Cette limitation est analogue à une restriction similaire appliquée à l'utilisation de importScripts(). Les appels dynamiques à importScripts() ne fonctionnent pas dans un service worker, et tous les appels importScripts(), qui sont par nature synchrones, doivent être terminés avant que le service worker termine sa phase install. Cette restriction garantit que le navigateur connaît et peut mettre en cache implicitement tout le code JavaScript nécessaire à l'implémentation d'un service worker lors de l'installation.

À terme, cette restriction peut être levée et les importations de modules ES dynamiques peuvent être autorisées. Pour l'instant, veillez à n'utiliser la syntaxe statique que dans un service worker.

Qu'en est-il des autres nœuds de calcul ?

La prise en charge des modules ES dans des nœuds de calcul "dédiés" (ceux créés avec new Worker('...', {type: 'module'})) est plus répandue. Elle est compatible avec Chrome et Edge depuis la version 80, ainsi que dans les versions récentes de Safari. Les importations de modules ES statiques et dynamiques sont compatibles avec les nœuds de calcul dédiés.

Chrome et Edge sont compatibles avec les modules ES dans les nœuds de calcul partagés depuis la version 83, mais aucun autre navigateur n'est compatible pour le moment.

Impossible d'importer des cartes

Les mappages d'importation permettent aux environnements d'exécution de réécrire les spécificateurs de module afin, par exemple, d'ajouter en préfixe l'URL d'un CDN préféré à partir duquel les modules ES peuvent être chargés.

Bien que Chrome et Edge version 89 ou ultérieure soient compatibles avec l'importation de cartes, ils ne peuvent actuellement pas être utilisés avec les service workers.

Prise en charge des navigateurs

Les modules ES des service workers sont compatibles avec Chrome et Edge à partir de la version 91.

Safari est désormais compatible avec la version Preview technologique 122. Les développeurs doivent s'attendre à ce que cette fonctionnalité soit publiée dans la version stable de Safari à l'avenir.

Exemple de code

Voici un exemple basique d'utilisation d'un module ES partagé dans le contexte window d'une application Web, tout en enregistrant un service worker utilisant le même module ES:

// 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);
    // ...
  })());
});

Rétrocompatibilité

L'exemple ci-dessus fonctionnerait si tous les navigateurs acceptaient les modules ES dans les service workers. Cependant, à ce stade, ce n'est pas le cas.

Pour être compatible avec les navigateurs qui ne sont pas compatibles, vous pouvez exécuter votre script Service worker via un bundler compatible avec le module ES pour créer un service worker qui inclut tout le code du module de manière intégrée et fonctionne dans les anciens navigateurs. Si les modules que vous essayez d'importer sont déjà disponibles sous forme de lots aux formats IIFE ou UMD, vous pouvez les importer à l'aide de importScripts().

Une fois que deux versions de votre service worker sont disponibles (l'une qui utilise des modules ES et l'autre non), vous devez détecter la compatibilité du navigateur actuel et enregistrer le script de service worker correspondant. Les bonnes pratiques de détection de la compatibilité sont en cours de développement, mais vous pouvez suivre la discussion dans ce problème GitHub pour obtenir des recommandations.

_Photo par Vlado Paunovic sur Unsplash_