Service workers et modèle de shell d'application

Une fonctionnalité architecturale courante des applications Web monopages (SPA) est un ensemble minimal de fichiers HTML, CSS et JavaScript nécessaires au fonctionnement global d'une application. Dans la pratique, il s'agit généralement de l'en-tête, de la navigation et d'autres éléments courants de l'interface utilisateur, qui sont conservés sur toutes les pages. Lorsqu'un service worker met en pré-cache le code HTML de cette interface utilisateur minimale et les éléments qui en dépendent, nous appelons cela le shell d'application.

Schéma d'un shell d'application. Il s'agit de la capture d'écran d'une page Web avec un en-tête en haut et une zone de contenu en bas. L'en-tête est intitulé "Application Shell", tandis que le bas est intitulé "Content".

Le shell d'application joue un rôle important dans la perception des performances d'une application Web. C'est la première chose qui se charge. Par conséquent, c'est aussi la première chose que les utilisateurs voient en attendant que le contenu apparaisse dans l'interface utilisateur.

Même si le shell de l'application est rapide à charger (à condition que le réseau soit disponible et au moins assez rapide), un service worker qui met en pré-cache le shell de l'application et ses éléments associés offre au modèle de shell d'application les avantages supplémentaires suivants:

  • Performances fiables et constantes lors des visites répétées. Lors de la première visite sur une application pour laquelle aucun service worker n'est installé, son balisage et les éléments associés doivent être chargés depuis le réseau pour que le service worker puisse les mettre dans son cache. Toutefois, en cas de visites répétées, le shell de l'application est extrait du cache, ce qui signifie que le chargement et l'affichage sont instantanés.
  • Accès fiable aux fonctionnalités hors connexion. Parfois, l'accès à Internet est instable, voire inexistant, et le redouté « nous ne pouvons pas trouver ce site web » à l'arrière. Le modèle shell d'application résout ce problème en répondant à toute requête de navigation avec le balisage du shell d'application à partir du cache. Même si un utilisateur consulte une URL dans votre application Web à laquelle il n'a jamais accédé auparavant, le shell de l'application est diffusé à partir du cache et peut être rempli avec du contenu utile.

Quand utiliser le modèle shell de l'application

Un shell d'application est particulièrement judicieux lorsque vous disposez d'éléments d'interface utilisateur courants qui ne changent pas d'une route à l'autre, mais qui le sont. La plupart des SPA utilisent probablement ce qui est déjà effectivement un modèle shell d'application.

Si cette description correspond à votre projet et que vous souhaitez ajouter un service worker pour améliorer sa fiabilité et ses performances, le shell de l'application doit:

  • Un chargement rapide :
  • Utilisez des éléments statiques à partir d'une instance Cache.
  • Incluez des éléments d'interface courants, tels qu'un en-tête et une barre latérale, séparés du contenu de la page.
  • Récupérez et affichez du contenu spécifique à la page.
  • Le cas échéant, mettez en cache le contenu dynamique pour un visionnage hors connexion.

Le shell de l'application charge dynamiquement le contenu spécifique à la page via des API ou du contenu groupé en JavaScript. Il doit également se mettre à jour automatiquement, dans le sens où si le balisage du shell de l'application change, une mise à jour de service worker doit récupérer le nouveau shell d'application et le mettre en cache automatiquement.

Créer le shell de l'application

Le shell de l'application doit exister indépendamment du contenu, tout en fournissant une base pour y insérer le contenu. Idéalement, il doit être le plus fin possible, mais le téléchargement initial doit inclure suffisamment de contenu pertinent pour que l'utilisateur comprenne que l'expérience se charge rapidement.

Le bon équilibre dépend de votre application. Le shell de l'application Trained To Thrill de Jake Archibald comprend un en-tête avec un bouton d'actualisation permettant d'intégrer de nouveaux contenus Flickr.

Capture d'écran de l'application Web Trained to Thrill dans deux états différents. À gauche, seul le shell de l'application mis en cache est visible, et aucun contenu n'est renseigné. À droite, le contenu (quelques images de certains trains) est chargé de manière dynamique dans la zone de contenu du shell d'application.

Le balisage du shell de l'application varie d'un projet à l'autre, mais voici un exemple de fichier index.html qui fournit le code récurrent de l'application:

​​<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      Application Shell Example
    </title>
    <link rel="manifest" href="/manifest.json">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="styles/global.css">
  </head>
  <body>
    <header class="header">
      <!-- Application header -->
      <h1 class="header__title">Application Shell Example</h1>
    </header>

    <nav class="nav">
      <!-- Navigation items -->
    </nav>

    <main id="app">
      <!-- Where the application content populates -->
    </main>

    <div class="loader">
      <!-- Spinner/content placeholders -->
    </div>

    <!-- Critical application shell logic -->
    <script src="app.js"></script>

    <!-- Service worker registration script -->
    <script>
      if ('serviceWorker' in navigator) {
        // Register a service worker after the load event
        window.addEventListener('load', () => {
          navigator.serviceWorker.register('/sw.js');
        });
      }
    </script>
  </body>
</html>

Quelle que soit la façon dont vous créez un shell d'application pour votre projet, il doit présenter les caractéristiques suivantes:

  • Le code HTML doit comporter des zones clairement isolées pour les différents éléments de l'interface utilisateur. Dans l'exemple ci-dessus, cela inclut l'en-tête, la navigation, la zone de contenu principal et l'espace pour un "spinner" de chargement. qui s'affiche uniquement lors du chargement du contenu.
  • Le code JavaScript et CSS initial chargé pour le shell de l'application doit être minimal et ne concerner que la fonctionnalité du shell lui-même, et non le contenu. Cela garantit que l'application affiche son shell aussi rapidement que possible et minimise le travail du thread principal jusqu'à ce que le contenu s'affiche.
  • Script intégré qui enregistre un service worker.

Une fois le shell d'application créé, vous pouvez créer un service worker pour le mettre en cache, ainsi que ses éléments.

Mettre en cache le shell d'application

Le shell de l'application et les éléments requis correspondent à ce que le service worker doit mettre en cache immédiatement au moment de l'installation. En supposant que vous utilisiez un shell d'application semblable à l'exemple ci-dessus, voyons comment procéder dans un exemple de base Workbox avec workbox-build:

// build-sw.js
import {generateSW} from 'workbox-build';

// Where the generated service worker will be written to:
const swDest = './dist/sw.js';

generateSW({
  swDest,
  globDirectory: './dist',
  globPatterns: [
    // The necessary CSS and JS for the app shell
    '**/*.js',
    '**/*.css',
    // The app shell itself
    'shell.html'
  ],
  // All navigations for URLs not precached will use this HTML
  navigateFallback: 'shell.html'
}).then(({count, size}) => {
  console.log(`Generated ${swDest}, which precaches ${count} assets totaling ${size} bytes.`);
});

Cette configuration stockée dans build-sw.js importe les fichiers CSS et JavaScript de l'application, y compris le fichier de balisage du shell de l'application contenu dans shell.html. Le script est exécuté avec Node comme suit:

node build-sw.js

Le service worker généré est écrit dans ./dist/sw.js et consignera le message suivant une fois l'opération terminée:

Generated ./dist/sw.js, which precaches 5 assets totaling 44375 bytes.

Lors du chargement de la page, le service worker met en pré-cache le balisage du shell de l'application et ses dépendances:

<ph type="x-smartling-placeholder">
</ph> Capture d&#39;écran du panneau &quot;Network&quot; (Réseau) dans les outils de développement de Chrome montrant la liste des éléments téléchargés depuis le réseau. Les éléments mis en pré-cache par le service worker se distinguent des autres ressources grâce à une roue dentée à gauche sur la ligne. Plusieurs fichiers JavaScript et CSS sont mis en pré-cache par le service worker au moment de l&#39;installation.
Le service worker met en pré-cache les dépendances du shell de l'application au moment de l'installation. Les requêtes de mise en cache préalable correspondent aux deux dernières lignes. L'icône en forme de roue dentée située à côté de la requête indique que le service worker a traité celle-ci.

La mise en cache préalable du code HTML, CSS et JavaScript du shell d'application est possible dans presque tous les workflows, y compris dans les projets utilisant des bundles. À mesure que vous parcourez la documentation, vous apprendrez à utiliser directement Workbox pour configurer votre chaîne d'outils afin de créer un service worker adapté à votre projet, qu'il s'agisse d'une application monopage.

Conclusion

La combinaison du modèle de shell d'application et d'un service worker est idéale pour la mise en cache hors connexion, en particulier si vous combinez sa fonctionnalité de mise en cache préalable avec une stratégie axée sur le réseau, en revenant à une stratégie de cache pour le balisage ou les réponses de l'API. Vous bénéficiez ainsi d'une expérience rapide et fiable, qui affiche instantanément le shell de votre application lors de vos visites répétées, même hors connexion.