Service Worker und das Anwendungs-Shell-Modell

Ein gemeinsames Architekturmerkmal von Single-Page-Webanwendungen (SPA) ist ein minimaler Satz an HTML, CSS und JavaScript, der für die globale Funktionalität einer Anwendung erforderlich ist. In der Praxis sind dies in der Regel die Kopfzeile, die Navigation und andere gängige Elemente der Benutzeroberfläche, die über alle Seiten hinweg bestehen bleiben. Wenn ein Service Worker den HTML-Code und die abhängigen Assets dieser minimalen UI vorab im Cache speichert, nennen wir dies Anwendungsshell.

Ein Diagramm einer Anwendungs-Shell. Es ist ein Screenshot einer Webseite mit einer Kopfzeile oben und einem Inhaltsbereich unten. Der Header trägt die Bezeichnung „Application Shell“, während der untere Teil mit „Content“ beschriftet ist.

Die Anwendungs-Shell spielt eine wichtige Rolle für die wahrgenommene Leistung einer Webanwendung. Es ist das erste, was geladen wird. Daher ist es auch das Erste, was Nutzende sehen, während sie auf den Inhalt der Benutzeroberfläche warten.

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

  • Zuverlässige, konsistente Leistung bei wiederholten Besuchen: Beim ersten Aufruf einer App, für die kein Service Worker installiert ist, müssen das Markup der Anwendung und die zugehörigen Assets aus dem Netzwerk geladen werden, bevor sie vom Service Worker in den Cache gespeichert werden können. Bei wiederholten Besuchen wird jedoch die Anwendungs-Shell aus dem Cache abgerufen, was bedeutet, dass das Laden und Rendern sofort erfolgt.
  • Zuverlässiger Zugriff auf Funktionen auch in Offlineszenarien Manchmal ist der Internetzugang nur schlecht oder gar nicht vorhanden und die gefürchtete „wir können diese Website nicht finden“ der Bildschirm streckt sich auf. Das Anwendungs-Shell-Modell löst dieses Problem, indem es auf alle Navigationsanfragen mit dem Anwendungs-Shell-Markup aus dem Cache antwortet. Selbst wenn jemand eine URL in Ihrer Webanwendung besucht, die er noch nie zuvor besucht hat, wird die Anwendungs-Shell aus dem Cache bedient 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 über gängige Elemente der Benutzeroberfläche verfügen, die sich bei Route zu Route nicht ändern, der Inhalt jedoch schon. Die meisten SPAs nutzen wahrscheinlich bereits ein Anwendungs-Shell-Modell.

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

  • Schnell laden:
  • Verwenden Sie statische Assets aus einer Cache-Instanz.
  • Binden Sie gängige Oberflächenelemente wie eine Kopfzeile und eine Seitenleiste getrennt vom Inhalt der Seite ein.
  • Seitenspezifische Inhalte abrufen und anzeigen.
  • Speichern Sie gegebenenfalls dynamische Inhalte für die Offlinewiedergabe im Cache.

Die Anwendungs-Shell lädt seitenspezifische Inhalte dynamisch über APIs oder Inhalte, die in JavaScript gebündelt sind. Sie sollte auch selbst aktualisiert werden, in dem Sinne, dass eine Service Worker-Aktualisierung die neue Anwendungs-Shell erfassen und automatisch zwischenspeichern sollte, wenn sich das Markup der Anwendungs-Shell ändert.

Anwendungs-Shell erstellen

Die Anwendungs-Shell sollte unabhängig vom Inhalt vorhanden sein, aber dennoch die Grundlage für die darin enthaltenen Inhalte bieten. Idealerweise sollte es so schlank wie möglich sein, aber ausreichend aussagekräftige Inhalte in den ersten Download aufnehmen, sodass der Nutzer weiß, dass das Erlebnis schnell geladen wird.

Die richtige Balance hängt von Ihrer App ab. Die Anwendungs-Shell für die Trained To Thrill App von Jake Archibald enthält einen Header mit einer Schaltfläche zum Aktualisieren, über die neue Inhalte aus Flickr geladen werden können.

Screenshot der Web-App „Trained to Thrill“ in zwei verschiedenen Status Links ist nur die im Cache gespeicherte Anwendungs-Shell und kein Inhalt sichtbar. Auf der rechten Seite werden die Inhalte (einige Bilder einiger Züge) dynamisch in den Inhaltsbereich der Anwendungs-Shell geladen.

Das Markup der Anwendungs-Shell variiert von Projekt zu Projekt. Hier ist ein Beispiel für eine index.html-Datei, die den Standardcode der Anwendung 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 davon, wie Sie eine Anwendungs-Shell für Ihr Projekt erstellen, muss sie die folgenden Eigenschaften haben:

  • Der HTML-Code sollte klar isolierte Bereiche für die einzelnen Elemente der Benutzeroberfläche enthalten. Im obigen Beispiel umfasst dies die Kopfzeile, die Navigation, den Hauptinhaltsbereich und den Platz für ein Lade-„spinner“ der Anwendung die nur beim Laden von Inhalten angezeigt wird.
  • Der anfängliche JavaScript- und CSS-Code, der für die Anwendungs-Shell geladen wird, sollte minimal sein und bezieht sich nur auf die Funktionalität der Anwendungs-Shell selbst, nicht auf den Inhalt. Dadurch wird sichergestellt, dass die Anwendung ihre Shell so schnell wie möglich rendert und die Arbeit mit dem Hauptthread minimiert wird, bis der Inhalt angezeigt wird.
  • Ein Inline-Skript, das einen Service Worker registriert.

Nachdem die Anwendungs-Shell erstellt wurde, können Sie einen Service Worker erstellen, um sowohl die Anwendungs-Shell als auch die zugehörigen Assets im Cache zu speichern.

Anwendungs-Shell im Cache speichern

Der Service Worker sollte bei der Installation sofort die Anwendungs-Shell und die erforderlichen Assets im Cache speichern. Bei einer Anwendungs-Shell wie dem obigen Beispiel sehen wir uns anhand eines einfachen Workbox-Beispiels mit workbox-build an, wie dies 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.`);
});

Diese in build-sw.js gespeicherte Konfiguration importiert den CSS- und JavaScript-Code der App, einschließlich der in shell.html enthaltenen Shell-Markup-Datei der Anwendung. Das Skript wird so mit Node ausgeführt:

node build-sw.js

Der generierte Service Worker wird in ./dist/sw.js geschrieben und protokolliert nach Abschluss die folgende Meldung:

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 die zugehörigen Abhängigkeiten vorab im Cache:

<ph type="x-smartling-placeholder">
</ph> Screenshot des Bereichs „Netzwerk“ in den Chrome-Entwicklertools mit einer Liste der aus dem Netzwerk heruntergeladenen Assets Assets, die vom Service Worker vorab im Cache gespeichert wurden, 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 zeigt an, dass der Service Worker die Anfrage bearbeitet hat.

Das Vorabspeichern des HTML-, CSS- und JavaScript-Codes Ihrer Anwendungs-Shell ist in fast jedem Workflow möglich, auch in Projekten mit Bundlern. Während Sie die Dokumentation durcharbeiten, erfahren Sie, wie Sie Workbox direkt verwenden, um Ihre Toolchain einzurichten, um einen Service Worker zu erstellen, 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 wenn Sie dessen Precaching-Funktion mit einer Netzwerk-First-, Fallback auf die Cache-Strategie für Markup- oder API-Antworten kombinieren. Auf diese Weise wird Ihre Anwendungs-Shell bei wiederholten Besuchen sofort gerendert, selbst wenn Sie offline sind.