Service worker e modello shell dell'applicazione

Una caratteristica architetturale comune delle applicazioni web a pagina singola (APS) è un insieme minimo di HTML, CSS e JavaScript necessario per alimentare la funzionalità globale di un'applicazione. In pratica, tendono a essere l'intestazione, la navigazione e altri elementi comuni dell'interfaccia utente che rimangono su tutte le pagine. Quando un service worker pre-memorizza nella cache l'HTML di questa UI minima e gli asset dipendenti, si parla di shell dell'applicazione.

Diagramma della shell di un'applicazione. È uno screenshot di una pagina web con un'intestazione nella parte superiore e un'area dei contenuti in basso. L'intestazione è etichettata "Application Shell", mentre quella in basso è etichettata come "Content".

La shell dell'applicazione svolge un ruolo significativo nella percezione delle prestazioni di un'applicazione web. È la prima cosa che si carica e, di conseguenza, è anche la prima cosa che gli utenti vedono mentre attendono che i contenuti vengano visualizzati nell'interfaccia utente.

Mentre la shell dell'applicazione si carica rapidamente (a condizione che la rete sia disponibile e sia almeno un po' veloce), un service worker che prememorizza la shell dell'applicazione e i relativi asset associati offre al modello shell dell'applicazione questi vantaggi aggiuntivi:

  • Rendimento affidabile e costante nelle visite ripetute. Alla prima visita a un'app senza che sia installato un service worker, il markup dell'applicazione e gli asset associati devono essere caricati dalla rete prima che il service worker possa inserirlo nella cache. Tuttavia, visite ripetute estrarranno la shell dell'applicazione dalla cache, il che significa che il caricamento e il rendering saranno istantanei.
  • Accesso affidabile alle funzionalità in scenari offline. A volte l'accesso a internet è discontinuo o assente del tutto e il temuto "non riusciamo a trovare il sito web" schermo a testa in giù. Il modello shell dell'applicazione risolve questo problema rispondendo a qualsiasi richiesta di navigazione con il markup della shell dell'applicazione dalla cache. Anche se un utente visita nella tua app web un URL che non ha mai visitato prima, la shell dell'applicazione viene pubblicata dalla cache e può essere compilata con contenuti utili.

Quando deve essere utilizzato il modello shell dell'applicazione

Una shell dell'applicazione ha più senso quando hai elementi dell'interfaccia utente comuni che non cambiano da un percorso all'altro, mentre i contenuti sì. La maggior parte delle APS probabilmente utilizza già in modo efficace un modello shell dell'applicazione.

Se questo descrive il tuo progetto e vuoi aggiungere un service worker per migliorarne l'affidabilità e le prestazioni, la shell dell'applicazione deve:

  • Caricamento veloce.
  • Utilizza asset statici di un'istanza Cache.
  • Includi elementi comuni dell'interfaccia, come l'intestazione e la barra laterale, separati dai contenuti della pagina.
  • Recupera e visualizza contenuti specifici della pagina.
  • Se opportuno, memorizza nella cache i contenuti dinamici per la visualizzazione offline.

La shell dell'applicazione carica in modo dinamico contenuti specifici della pagina tramite API o contenuti raggruppati in JavaScript. Dovrebbe anche aggiornarsi autonomamente, nel senso che se il markup della shell dell'applicazione cambia, un aggiornamento del service worker dovrebbe rilevare la nuova shell dell'applicazione e memorizzarla automaticamente nella cache.

crea la shell dell'applicazione

La shell dell'applicazione deve esistere indipendentemente dai contenuti, pur fornendo una base per il completamento dei contenuti al suo interno. Idealmente, il file dovrebbe essere il più sottile possibile, ma includere nel download iniziale abbastanza contenuti significativi in modo che l'utente capisca che un'esperienza si carica rapidamente.

Il giusto equilibrio dipende dall'app. La shell dell'applicazione per l'app Trained To Thrill di Jake Archibald include un'intestazione con un pulsante di aggiornamento per recuperare nuovi contenuti da Flickr.

Uno screenshot dell'app web Trained to Thrill in due diversi stati. A sinistra è visibile solo la shell dell'applicazione memorizzata nella cache, senza contenuti compilati. A destra, i contenuti (alcune immagini di alcuni treni) vengono caricati dinamicamente nell'area dei contenuti della shell dell'applicazione.

Il markup della shell dell'applicazione varia da progetto a progetto, ma ecco un esempio di file index.html che fornisce il boilerplate dell'applicazione:

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

Indipendentemente da come crei una shell dell'applicazione per il tuo progetto, deve avere le seguenti caratteristiche:

  • Il codice HTML deve contenere aree chiaramente isolate per i singoli elementi dell'interfaccia utente. Nell'esempio precedente, sono inclusi l'intestazione, la navigazione, l'area dei contenuti principali e lo spazio per una "rotellina" di caricamento dell'applicazione. che viene visualizzata solo durante il caricamento dei contenuti.
  • I file JavaScript e CSS caricati inizialmente per la shell dell'applicazione dovrebbero essere minimi e riguardare solo la funzionalità della shell dell'applicazione stessa e non i contenuti. In questo modo, l'applicazione esegue il rendering della shell il più velocemente possibile e riduce al minimo il lavoro del thread principale finché non vengono visualizzati i contenuti.
  • Uno script in linea che registra un service worker.

Una volta creata la shell dell'applicazione, puoi creare un service worker per memorizzare nella cache sia questa shell sia i relativi asset.

Memorizzazione nella cache della shell dell'applicazione

Il service worker deve prememorizzare immediatamente nella cache la shell dell'applicazione e gli asset richiesti al momento dell'installazione. Presumendo una shell dell'applicazione come nell'esempio precedente, vediamo come si può ottenere tutto questo in un esempio di Workbox di base utilizzando 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.`);
});

Questa configurazione memorizzata in build-sw.js importa il codice CSS e JavaScript dell'app, incluso il file di markup della shell dell'applicazione contenuto in shell.html. Lo script viene eseguito con il nodo in questo modo:

node build-sw.js

Il service worker generato viene scritto in ./dist/sw.js e al termine registrerà il seguente messaggio:

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

Quando la pagina viene caricata, il service worker pre-memorizza nella cache il markup della shell dell'applicazione e le sue dipendenze:

Uno screenshot del riquadro Network in DevTools di Chrome che mostra un elenco di asset scaricati dalla rete. Gli asset prememorizzati dal service worker vengono distinti dagli altri con un ingranaggio a sinistra nella riga. Diversi file JavaScript e CSS vengono prememorizzati nella cache dal service worker al momento dell&#39;installazione.
Il service worker prememorizza nella cache le dipendenze della shell dell'applicazione al momento dell'installazione. Le richieste di pre-memorizzazione nella cache sono le ultime due righe e l'icona a forma di ingranaggio accanto alla richiesta indica che la richiesta è stata gestita dal Service worker.

La pre-memorizzazione nella cache dell'HTML, del CSS e di JavaScript della shell dell'applicazione è possibile in quasi tutti i flussi di lavoro, inclusi i progetti che utilizzano i bundler. Man mano che procedi nella documentazione, imparerai a utilizzare direttamente Workbox per configurare la tua toolchain e creare un service worker che funzioni al meglio per il tuo progetto, indipendentemente dal fatto che si tratti o meno di un'APS.

Conclusione

Combinare il modello shell dell'applicazione con un service worker è ottimo per la memorizzazione nella cache offline, in particolare se combini la sua funzionalità di pre-memorizzazione nella cache con una strategia di rete, con ricorso alla cache per il markup o le risposte delle API. Il risultato è un'esperienza affidabile e veloce che esegue il rendering immediato della shell dell'applicazione a visite ripetute, anche in condizioni offline.