Service workers et modèle de shell d'application

Une caractéristique architecturale courante des applications Web monopages (SPA) est un ensemble minimal de code HTML, CSS et JavaScript nécessaire 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 l'ensemble des pages. Lorsqu'un service worker met en pré-cache le code HTML et les éléments dépendants de cette interface utilisateur minimale, nous appelons cela le shell de l'application.

Schéma d'un shell d'application. Il s'agit d'une 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 les performances perçues d'une application Web. C'est la première chose qui se charge. C'est donc la première chose que les utilisateurs voient en attendant que le contenu s'affiche dans l'interface utilisateur.

Bien que le shell de l'application se charge rapidement (à condition que le réseau soit disponible et au moins assez rapide), un service worker qui effectue une mise en cache préalable du shell de l'application et de ses éléments associés offre au modèle de shell d'application les avantages suivants:

  • Performances fiables et cohérentes lors des visites répétées. Lors de la première visite d'une application sans service worker installé, le balisage de l'application et ses éléments associés doivent être chargés depuis le réseau avant que le service worker puisse les placer dans son cache. Toutefois, les visites répétées extraient le shell de l'application 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 ou complètement absent, et l'écran redoutable "Nous ne trouvons pas ce site Web" s'échappe. Le modèle de shell d'application résout ce problème en répondant à toutes les requêtes de navigation à l'aide du 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.

Dans quels cas utiliser le modèle de shell d'application

Un shell d'application est particulièrement utile lorsque vous avez des éléments d'interface utilisateur communs qui ne changent pas d'une route à l'autre, mais que le contenu le change. La plupart des applications monopages utilisent probablement déjà ce qui est déjà un modèle de shell d'application.

Si cela décrit votre projet, et que vous souhaitez ajouter un service worker afin d'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.
  • inclure 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érer et afficher du contenu spécifique à la page
  • Le cas échéant, vous pouvez mettre en cache le contenu dynamique pour l'afficher hors connexion.

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

Créer l'interface système de l'application

Le shell de l'application doit exister indépendamment du contenu, tout en fournissant une base pour le contenu qui y sera inséré. Dans l'idéal, elle doit être la plus fine possible, mais inclure suffisamment de contenu pertinent dans le téléchargement initial 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 Archibalde comprend un en-tête avec un bouton d'actualisation permettant d'importer le nouveau contenu de Flickr.

Capture d'écran de l'application Web "Trained to Thrill" dans deux états différents. À gauche, seul le shell d'application mis en cache est visible, 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 de l'application.

Le balisage du shell d'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 chaque élément de l'interface utilisateur. Dans l'exemple ci-dessus, cela inclut l'en-tête de l'application, la navigation, la zone de contenu principal et l'espace réservé à un "icône de chargement" qui ne s'affiche que lors du chargement du contenu.
  • Le code JavaScript et CSS initial chargé pour le shell de l'application doit être minimal et se rapporter uniquement aux fonctionnalités de son propre shell, et non à son 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 de l'application créé, vous pouvez créer un service worker pour le mettre en cache, ainsi que ses éléments.

Mettre en cache le shell de l'application

Le shell de l'application et ses éléments requis sont ceux que le service worker doit mettre en cache immédiatement au moment de l'installation. En reprenant un shell d'application comme dans l'exemple ci-dessus, voyons comment procéder dans un exemple de Workbox de base à l'aide de 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 le code CSS et JavaScript de l'application, y compris le fichier de balisage du shell d'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 enregistre 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 cache le balisage du shell de l'application et ses dépendances:

Capture d&#39;écran du panneau &quot;Network&quot; (Réseau) dans les outils pour les développeurs Chrome montrant une liste d&#39;é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 éléments à l&#39;aide d&#39;un engrenage à 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 cache les dépendances de l'interface système de l'application au moment de l'installation. Les requêtes de mise en cache préalable correspondent aux deux dernières lignes, et l'icône en forme de roue dentée à côté de la requête indique que le service worker l'a traitée.

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 bundlers. À mesure que vous progresserez dans 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 ou non.

Conclusion

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