Sviluppo di app web progressive 02.0: guida rapida offline

Questo codelab fa parte del corso di formazione Sviluppare app web progressive, sviluppato dal team di formazione di Google Developers. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza.

Per i dettagli completi sul corso, consulta la panoramica sullo sviluppo di app web progressive.

Introduzione

In questo lab utilizzerai Lighthouse per controllare un sito web in base agli standard delle app web progressive (PWA). Aggiungerai anche la funzionalità offline con l'API Service Worker.

Obiettivi didattici

  • Come controllare i siti con Lighthouse
  • Come aggiungere funzionalità offline a un'applicazione

Che cosa devi sapere

  • HTML, CSS e JavaScript di base
  • Familiarità con le promesse ES2015

Che cosa ti serve

  • Computer con accesso al terminale/alla shell
  • Connessione a internet
  • Browser Chrome (per utilizzare Lighthouse)
  • Un editor di testo
  • (Facoltativo) Chrome su un dispositivo Android

Scarica o clona il repository pwa-training-labs da GitHub e installa la versione LTS di Node.js, se necessario.

Vai alla directory offline-quickstart-lab/app/ e avvia un server di sviluppo locale:

cd offline-quickstart-lab/app
npm install
node server.js

Puoi terminare il server in qualsiasi momento con Ctrl-c.

Apri il browser e vai all'indirizzo localhost:8081/. Dovresti vedere che il sito è una pagina web semplice e statica.

Nota:annulla la registrazione di tutti i service worker e svuota tutte le cache dei service worker per localhost in modo che non interferiscano con il lab. In Chrome DevTools, puoi farlo facendo clic su Cancella dati sito nella sezione Cancella spazio di archiviazione della scheda Applicazione.

Apri la cartella offline-quickstart-lab/app/ nell'editor di testo che preferisci. La cartella app/ è quella in cui creerai il lab.

Questa cartella contiene:

  • La cartella images/ contiene immagini di esempio
  • styles/main.css è il foglio di stile principale
  • index.html è la pagina HTML principale del nostro sito di esempio
  • package-lock.json e package.json monitorano le dipendenze dell'app (in questo caso, le uniche dipendenze sono per il server di sviluppo locale)
  • server.js è un server di sviluppo locale per i test
  • service-worker.js è il file service worker (attualmente vuoto)

Prima di iniziare ad apportare modifiche al sito, eseguiamo un controllo con Lighthouse per vedere cosa può essere migliorato.

Torna all'app (in Chrome) e apri la scheda Audit degli strumenti per sviluppatori. Dovresti visualizzare l'icona di Lighthouse e le opzioni di configurazione. Seleziona "Dispositivo mobile" per Dispositivo, seleziona tutti gli Audit, seleziona una delle opzioni di Limitazione e scegli di Cancellare lo spazio di archiviazione:

Fai clic su Esegui controlli. Il completamento degli audit richiede alcuni istanti.

Spiegazione

Una volta completato l'audit, dovresti visualizzare un report con i punteggi in Strumenti per sviluppatori. Dovrebbero essere visualizzati i punteggi, in modo simile a questo (i punteggi potrebbero non essere esattamente gli stessi):

Nota:i punteggi di Lighthouse sono approssimativi e possono essere influenzati dall'ambiente (ad esempio, se hai aperto un numero elevato di finestre del browser). I tuoi punteggi potrebbero non corrispondere esattamente a quelli mostrati qui.

La sezione App web progressiva dovrebbe essere simile a questa:

Il report contiene punteggi e metriche in cinque categorie:

  • App web progressiva
  • Rendimento
  • Accessibilità
  • Best practice
  • SEO

Come puoi vedere, la nostra app ha un punteggio basso nella categoria delle app web progressive (PWA). Miglioriamo il nostro punteggio.

Dai un'occhiata alla sezione PWA del report e verifica cosa manca.

Registrare un service worker

Uno degli errori elencati nel report è che non è registrato alcun service worker. Al momento abbiamo un file service worker vuoto in app/service-worker.js.

Aggiungi il seguente script in fondo a index.html, appena prima del tag di chiusura </body>:

<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('service-worker.js')
      .then(reg => {
        console.log('Service worker registered! 😎', reg);
      })
      .catch(err => {
        console.log('😥 Service worker registration failed: ', err);
      });
  });
}
</script>

Spiegazione

Questo codice registra il file service worker service-worker.js vuoto una volta caricata la pagina. Tuttavia, il file service worker corrente è vuoto e non farà nulla. Aggiungiamo il codice di servizio nel passaggio successivo.

Risorse di prememorizzazione nella cache

Un altro errore elencato nel report è che l'app non risponde con un codice di stato 200 quando è offline. Per risolvere il problema, dobbiamo aggiornare il service worker.

Aggiungi il seguente codice al file service worker (service-worker.js):

const cacheName = 'cache-v1';
const precacheResources = [
  '/',
  'index.html',
  'styles/main.css',
  'images/space1.jpg',
  'images/space2.jpg',
  'images/space3.jpg'
];

self.addEventListener('install', event => {
  console.log('Service worker install event!');
  event.waitUntil(
    caches.open(cacheName)
      .then(cache => {
        return cache.addAll(precacheResources);
      })
  );
});

self.addEventListener('activate', event => {
  console.log('Service worker activate event!');
});

self.addEventListener('fetch', event => {
  console.log('Fetch intercepted for:', event.request.url);
  event.respondWith(caches.match(event.request)
    .then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse;
        }
        return fetch(event.request);
      })
    );
});

Ora torna al browser e aggiorna il sito. Controlla la console per verificare che il service worker:

  • registrato
  • installata
  • attivato

Nota:se hai già registrato il service worker in precedenza o hai difficoltà a far attivare tutti gli eventi, annulla la registrazione di tutti i service worker e aggiorna la pagina. Se l'operazione non va a buon fine, chiudi tutte le istanze dell'app e riaprila.

Successivamente, termina il server di sviluppo locale nella riga di comando eseguendo Ctrl + c. Aggiorna di nuovo il sito e osserva che si carica anche se il server è offline.

Nota:potresti visualizzare un errore della console che indica che non è stato possibile recuperare il service worker: An unknown error occurred when fetching the script. service-worker.js Failed to load resource: net::ERR_CONNECTION_REFUSED. Questo errore viene visualizzato perché il browser non è riuscito a recuperare lo script del service worker (perché il sito è offline), ma è previsto perché non possiamo utilizzare il service worker per memorizzare nella cache se stesso. Altrimenti, il service worker rimarrebbe bloccato per sempre nel browser dell'utente.

Spiegazione

Una volta registrato il service worker dallo script di registrazione in index.html, si verifica l'evento install del service worker. Durante questo evento, il listener di eventi install apre una cache denominata e memorizza nella cache i file specificati con il metodo cache.addAll. Questa operazione viene chiamata "pre-caching" perché si verifica durante l'evento install, che in genere si verifica la prima volta che un utente visita il tuo sito.

Dopo l'installazione di un service worker e se un altro service worker non controlla attualmente la pagina, il nuovo service worker viene "attivato" (viene attivato il listener di eventi activate nel service worker) e inizia a controllare la pagina.

Quando le risorse vengono richieste da una pagina controllata da un service worker attivato, le richieste passano attraverso il service worker, come un proxy di rete. Per ogni richiesta viene attivato un evento fetch. Nel nostro service worker, il listener di eventi fetch cerca nelle cache e risponde con la risorsa memorizzata nella cache, se disponibile. Se la risorsa non è memorizzata nella cache, viene richiesta normalmente.

La memorizzazione nella cache delle risorse consente all'app di funzionare offline evitando le richieste di rete. Ora la nostra app può rispondere con un codice di stato 200 quando è offline.

Nota:l'evento di attivazione non viene utilizzato per altro oltre all'accesso in questo esempio. L'evento è stato incluso per facilitare il debug dei problemi del ciclo di vita dei service worker.

(Facoltativo): puoi anche visualizzare le risorse memorizzate nella cache nella scheda Applicazione degli Strumenti per sviluppatori espandendo la sezione Archiviazione cache:

Riavvia il server di sviluppo con node server.js e aggiorna il sito. Poi apri di nuovo la scheda Audit in Strumenti per gli sviluppatori ed esegui di nuovo l'audit Lighthouse selezionando Nuovo audit (il segno più nell'angolo in alto a sinistra). Al termine dell'audit, dovresti notare che il nostro punteggio PWA è notevolmente migliore, ma potrebbe comunque essere migliorato. Continueremo a migliorare il nostro punteggio nella sezione seguente.

Nota:questa sezione è facoltativa perché il test del banner di installazione dell'app web non rientra nell'ambito del lab. Puoi provare a farlo da solo utilizzando il debug remoto.

Il nostro punteggio PWA non è ancora ottimo. Alcuni degli errori rimanenti elencati nel report sono che all'utente non verrà chiesto di installare la nostra app web e che non abbiamo configurato una schermata iniziale o i colori del brand nella barra degli indirizzi. Possiamo risolvere questi problemi e implementare progressivamente la funzionalità Aggiungi alla schermata Home soddisfacendo alcuni criteri aggiuntivi. Ancora più importante, dobbiamo creare un file manifest.

Creare un file manifest

Crea un file denominato manifest.json in app/ e aggiungi il seguente codice:

{
  "name": "Space Missions",
  "short_name": "Space Missions",
  "lang": "en-US",
  "start_url": "/index.html",
  "display": "standalone",
  "theme_color": "#FF9800",
  "background_color": "#FF9800",
  "icons": [
    {
      "src": "images/touch/icon-128x128.png",
      "sizes": "128x128"
    },
    {
      "src": "images/touch/icon-192x192.png",
      "sizes": "192x192"
    },
    {
      "src": "images/touch/icon-256x256.png",
      "sizes": "256x256"
    },
    {
      "src": "images/touch/icon-384x384.png",
      "sizes": "384x384"
    },
    {
      "src": "images/touch/icon-512x512.png",
      "sizes": "512x512"
    }
  ]
}

Le immagini a cui viene fatto riferimento nel manifest sono già fornite nell'app.

Aggiungi quindi il seguente codice HTML in fondo al tag <head> in index.html:

<link rel="manifest" href="manifest.json">

<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Space Missions">
<meta name="apple-mobile-web-app-title" content="Space Missions">
<meta name="theme-color" content="#FF9800">
<meta name="msapplication-navbutton-color" content="#FF9800">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/index.html">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<link rel="icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="apple-touch-icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="icon" sizes="192x192" href="icon-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/touch/icon-192x192.png">
<link rel="icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="apple-touch-icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="apple-touch-icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="icon" sizes="512x512" href="/images/touch/icon-512x512.png">
<link rel="apple-touch-icon" sizes="512x512" href="/images/touch/icon-512x512.png">

Torna al sito. Nella scheda Applicazione degli Strumenti per sviluppatori, seleziona la sezione Cancella spazio di archiviazione e fai clic su Cancella dati del sito. Poi aggiorna la pagina. Ora seleziona la sezione Manifesto. Dovresti visualizzare le icone e le opzioni di configurazione configurate nel file manifest.json. Se non vedi le modifiche, apri il sito in una finestra di navigazione in incognito e controlla di nuovo.

Spiegazione

Il file manifest.json indica al browser come applicare lo stile e formattare alcuni aspetti progressivi dell'app, come la cromatura del browser, l'icona della schermata Home e la schermata iniziale. Può essere utilizzato anche per configurare l'apertura dell'app web in modalità standalone, come un'app nativa (ovvero al di fuori del browser).

Il supporto è ancora in fase di sviluppo per alcuni browser al momento della stesura di questo articolo e i tag <meta> configurano un sottoinsieme di queste funzionalità per alcuni browser che non hanno ancora il supporto completo.

Abbiamo dovuto cancellare i dati del sito per rimuovere la vecchia versione memorizzata nella cache di index.html (poiché questa versione non aveva il link al manifest). Prova a eseguire un altro controllo Lighthouse e verifica di quanto è migliorato il punteggio PWA.

Attivazione della richiesta di installazione

Il passaggio successivo per l'installazione della nostra app consiste nel mostrare agli utenti il prompt di installazione. Chrome 67 richiedeva automaticamente agli utenti di installare l'app, ma a partire da Chrome 68, il prompt di installazione deve essere attivato a livello di programmazione in risposta a un gesto dell'utente.

Aggiungi un pulsante e un banner "Installa app" nella parte superiore di index.html (subito dopo il tag <main>) con il seguente codice:

<section id="installBanner" class="banner">
    <button id="installBtn">Install app</button>
</section>

Poi, dai uno stile al banner aggiungendo gli stili seguenti a styles/main.css:

.banner {
  align-content: center;
  display: none;
  justify-content: center;
  width: 100%;
}

Salva il file. Infine, aggiungi il seguente tag script a index.html:

  <script>
    let deferredPrompt;
    window.addEventListener('beforeinstallprompt', event => {

      // Prevent Chrome 67 and earlier from automatically showing the prompt
      event.preventDefault();

      // Stash the event so it can be triggered later.
      deferredPrompt = event;

      // Attach the install prompt to a user gesture
      document.querySelector('#installBtn').addEventListener('click', event => {

        // Show the prompt
        deferredPrompt.prompt();

        // Wait for the user to respond to the prompt
        deferredPrompt.userChoice
          .then((choiceResult) => {
            if (choiceResult.outcome === 'accepted') {
              console.log('User accepted the A2HS prompt');
            } else {
              console.log('User dismissed the A2HS prompt');
            }
            deferredPrompt = null;
          });
      });

      // Update UI notify the user they can add to home screen
      document.querySelector('#installBanner').style.display = 'flex';
    });
  </script>

Salva il file. Apri l'app in Chrome su un dispositivo Android utilizzando il debug remoto. Quando la pagina viene caricata, dovresti visualizzare il pulsante "Installa app" (non lo vedrai su un computer, quindi assicurati di eseguire il test su un dispositivo mobile). Fai clic sul pulsante e dovrebbe essere visualizzato il prompt Aggiungi alla schermata Home. Segui i passaggi per installare l'app sul dispositivo. Dopo l'installazione, dovresti essere in grado di aprire la web app in modalità autonoma (al di fuori del browser) toccando l'icona della schermata Home appena creata.

Spiegazione

Il codice HTML e CSS aggiunge un banner e un pulsante nascosti che possiamo utilizzare per consentire agli utenti di attivare la richiesta di installazione.

Una volta attivato l'evento beforeinstallprompt, impediamo l'esperienza predefinita (in cui Chrome 67 e versioni precedenti chiedono automaticamente agli utenti di installare) e acquisiscono beforeinstallevent nella variabile globale deferredPrompt. Il pulsante "Installa app" viene quindi configurato per mostrare il prompt con il metodo prompt() di beforeinstallevent. Una volta che l'utente ha fatto una scelta (installare o meno), la promessa userChoice viene risolta con la scelta dell'utente (outcome). Infine, mostriamo il pulsante di installazione quando tutto è pronto.

Hai imparato a controllare i siti con Lighthouse e a implementare le nozioni di base della funzionalità offline. Se hai completato le sezioni facoltative, hai anche imparato a installare le app web nella schermata Home.

Altre risorse

Lighthouse è open source. Puoi creare un fork, aggiungere i tuoi test e segnalare bug. Lighthouse è disponibile anche come strumento a riga di comando per l'integrazione con i processi di compilazione.

Per visualizzare tutti i codelab del corso di formazione sulle PWA, consulta il codelab di benvenuto del corso.