Service Worker und das Anwendungs-Shell-Modell

Ein gemeinsames Architekturmerkmal von Single-Page-Webanwendungen (SPA) ist ein minimaler Satz von HTML-, CSS- und JavaScript-Code, der für die globale Funktionalität einer Anwendung benötigt wird. In der Praxis sind dies die Kopfzeile, die Navigation und andere gängige Elemente der Benutzeroberfläche, die auf allen Seiten vorhanden sind. Wenn ein Service Worker den HTML-Code und die abhängigen Assets dieser Minimal-UI vorab im Cache speichert, nennen wir dies Application Shell.

Diagramm einer Anwendungs-Shell. Es handelt sich um einen Screenshot einer Webseite mit einer Kopfzeile oben und einem Inhaltsbereich unten. Der Header trägt die Bezeichnung „Application Shell“, der unteren das Label „Content“.

Die Anwendungs-Shell spielt eine wichtige Rolle für die wahrgenommene Leistung einer Webanwendung. Er wird als Erstes geladen und ist daher auch das Erste, was Nutzende sehen, während sie darauf warten, dass der Inhalt auf der Benutzeroberfläche erscheint.

Die Anwendungs-Shell lässt sich schnell laden – vorausgesetzt, das Netzwerk ist verfügbar und zumindest relativ schnell. Ein Service Worker, der die Anwendungs-Shell und die zugehörigen Assets vorab im Cache speichert, bietet dem Anwendungs-Shell-Modell folgende zusätzliche Vorteile:

  • Zuverlässige, konsistente Leistung bei wiederholten Besuchen Beim ersten Aufruf einer App ohne installierten Service Worker müssen das Markup der Anwendung und die zugehörigen Assets aus dem Netzwerk geladen werden, bevor der Service Worker sie in seinen Cache speichern kann. Bei wiederholten Besuchen wird jedoch die Anwendungs-Shell aus dem Cache abgerufen, was bedeutet, dass das Laden und Darstellen sofort erfolgt.
  • Zuverlässiger Zugriff auf Funktionen auch im Offlinemodus Manchmal ist der Internetzugang schlecht oder gar nicht vorhanden und der gefürchtete Bildschirm „Wir können diese Website nicht finden“ verwirrt den Kopf. Das Anwendungs-Shell-Modell löst dieses Problem, indem es auf jede Navigationsanfrage mit dem Anwendungs-Shell-Markup aus dem Cache antwortet. Selbst wenn jemand eine URL in Ihrer Web-App aufruft, die er noch nie zuvor besucht hat, wird die Anwendungs-Shell aus dem Cache bereitgestellt und kann mit nützlichen Inhalten gefüllt werden.

Wann das Anwendungs-Shell-Modell verwendet werden soll

Eine Anwendungs-Shell ist am sinnvollsten, wenn Sie allgemeine Benutzeroberflächenelemente haben, die sich zwar von einer Route zur nächsten nicht ändern, aber ihr Inhalt der Fall ist. Die meisten SPAs nutzen wahrscheinlich bereits ein Anwendungs-Shell-Modell.

Wenn dies Ihr Projekt beschreibt und Sie einen Service Worker hinzufügen möchten, um die Zuverlässigkeit und Leistung zu verbessern, sollte die Anwendungs-Shell:

  • Schnell.
  • Verwenden Sie statische Assets aus einer Cache-Instanz.
  • Fügen Sie gängige Elemente der Benutzeroberfläche wie eine Kopfzeile und eine Seitenleiste, die vom Inhalt der Seite getrennt sind, hinzu.
  • Seitenspezifische Inhalte abrufen und anzeigen
  • Optional können Sie dynamische Inhalte für die Offlinewiedergabe im Cache speichern.

Die Anwendungs-Shell lädt seitenspezifische Inhalte dynamisch über APIs oder in JavaScript gebündelte Inhalte. Es sollte sich auch selbst aktualisieren, wenn sich das Markup der Anwendungs-Shell ändert, und ein Service Worker-Update die neue Anwendungs-Shell abrufen und automatisch im Cache speichern soll.

Anwendungs-Shell erstellen

Die Anwendungs-Shell sollte unabhängig vom Inhalt vorhanden sein, jedoch eine Basis für Inhalte bieten, die darin gefüllt werden können. Idealerweise sollte er so schlank wie möglich sein, aber der erste Download sollte genügend aussagekräftige Inhalte enthalten, damit der Nutzer weiß, dass die Website schnell geladen wird.

Das richtige Gleichgewicht hängt von Ihrer App ab. Die App-Shell für die Trained To Thrill App von Jake Archibald enthält eine Kopfzeile mit einer Schaltfläche zum Aktualisieren, über die neue Inhalte von Flickr abgerufen werden können.

Screenshot der Web-App „Trained to Thrill“ in zwei verschiedenen Zuständen Links ist nur die im Cache gespeicherte Anwendungs-Shell sichtbar, ohne dass der Inhalt ausgefüllt ist. Auf der rechten Seite werden der Inhalt (ein paar Bilder von einigen Zügen) dynamisch in den Inhaltsbereich der Anwendungs-Shell geladen.

Das Markup der Anwendungs-Shell variiert von Projekt zu Projekt. Hier ist jedoch ein Beispiel für eine index.html-Datei, die den Anwendungs-Boilerplate-Code enthält:

​​<!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>

Unabhängig von der Erstellung einer Anwendungs-Shell für Ihr Projekt muss diese die folgenden Eigenschaften haben:

  • Der HTML-Code muss klar abgegrenzte Bereiche für die einzelnen Elemente der Benutzeroberfläche enthalten. Im obigen Beispiel umfasst dies den Header der Anwendung, die Navigation, den Hauptinhaltsbereich und den Bereich für ein Ladesymbol, das nur beim Laden des Inhalts angezeigt wird.
  • Der JavaScript- und CSS-Code, der anfangs für die App-Shell geladen wird, sollte minimal sein und sich nur auf die Funktionalität der App-Shell selbst und nicht auf den Inhalt beziehen. Dadurch wird die Shell der Anwendung so schnell wie möglich gerendert und die Arbeit des Hauptthreads minimiert, bis der Inhalt angezeigt wird.
  • Ein Inline-Skript, das einen Service Worker registriert.

Sobald die Anwendungs-Shell erstellt ist, können Sie einen Service Worker erstellen, der sowohl die Anwendungs-Shell als auch ihre Assets im Cache speichert.

Anwendungs-Shell im Cache speichern

Der Service Worker sollte die Anwendungs-Shell und ihre erforderlichen Assets sofort bei der Installation vorab im Cache speichern. Wenn wir von einer Anwendungs-Shell wie im obigen Beispiel ausgehen, sehen wir uns an, wie dies in einem einfachen Workbox-Beispiel mit workbox-build erreicht werden kann:

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

Mit dieser in build-sw.js gespeicherten Konfiguration werden der CSS- und JavaScript-Code der App importiert, einschließlich der in shell.html enthaltenen Anwendungs-Shell-Markup-Datei. Das Skript wird mit Node wie folgt ausgeführt:

node build-sw.js

Der generierte Service Worker wird in ./dist/sw.js geschrieben und protokolliert, wenn der Vorgang abgeschlossen ist:

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

Wenn die Seite geladen wird, speichert der Service Worker das Markup der Anwendungs-Shell und seine Abhängigkeiten vorab im Cache:

Screenshot des Bereichs „Netzwerk“ in den Entwicklertools von Chrome mit einer Liste der aus dem Netzwerk heruntergeladenen Assets. Vom Service Worker vorab im Cache gespeicherte Assets unterscheiden sich von anderen Assets mit einem Zahnrad links in der Zeile. Mehrere JavaScript- und CSS-Dateien werden vom Service Worker bei der Installation vorab im Cache gespeichert.
Der Service Worker speichert die Abhängigkeiten der Anwendungs-Shell bei der Installation vorab im Cache. Die Precaching-Anfragen befinden sich in den letzten beiden Zeilen. Das Zahnradsymbol neben der Anfrage gibt an, dass die Anfrage vom Service Worker verarbeitet wurde.

Das Precaching des HTML-, CSS- und JavaScript-Codes Ihrer Anwendungs-Shell ist in fast jedem Workflow möglich, einschließlich Projekten, bei denen Bundler verwendet werden. Im weiteren Verlauf der Dokumentation erfahren Sie, wie Sie Workbox direkt verwenden, um Ihre Toolchain so einzurichten, dass ein Service Worker erstellt wird, der für Ihr Projekt am besten funktioniert, unabhängig davon, ob es sich um eine SPA handelt.

Fazit

Die Kombination des Anwendungs-Shell-Modells mit einem Service Worker eignet sich hervorragend für das Offline-Caching, insbesondere dann, wenn Sie die Precaching-Funktion mit einer netzwerkorientierten, auf die Cache-Strategie zurückgreifen für Markups oder API-Antworten kombinieren. Das Ergebnis ist eine zuverlässig schnelle Erfahrung, die Ihre App-Shell bei wiederholten Besuchen sofort rendert, sogar offline.