Ciò che il team di bollettino ha imparato sui lavoratori dei servizi durante lo sviluppo di una PWA.
Questo è il primo di una serie di post del blog sulle lezioni apprese dal team di Google bollettino. durante la creazione di una PWA rivolta all'esterno. In questi post condivideremo alcune sfide che abbiamo affrontato, gli approcci che abbiamo adottato per superarli e i consigli generali per evitare le insidie. Non è affatto per una panoramica completa delle PWA. L'obiettivo è condividere quanto appreso dall'esperienza del nostro team.
In questo primo post, daremo prima alcune informazioni di base, poi illustreremo in dettaglio tutti i cosa abbiamo imparato sui service worker.
Sfondo
Il bollettino era in fase di sviluppo da metà 2017 a metà del 2019.
Perché abbiamo scelto di creare una PWA
Prima di approfondire il processo di sviluppo, vediamo perché lo sviluppo di una PWA è stato interessante per questo progetto:
- Capacità di iterazione rapida. Particolarmente importante perché bollettino sarebbe stato pilotato in in più mercati.
- Singolo codebase. I nostri utenti erano divisi quasi equamente tra Android e iOS. Una PWA intendeva potevamo creare un'unica app web che funzionasse su entrambe le piattaforme. Ciò ha aumentato la velocità e l'impatto del team.
- Aggiornamento rapido e indipendentemente dal comportamento degli utenti. le PWA possono aggiornare automaticamente riduce la quantità di client obsoleti in circolazione. Siamo riusciti a eseguire il push del backend di interruzione con un tempo di migrazione molto breve per i client.
- Facilmente integrato con app proprietarie e di terze parti. Queste integrazioni erano un requisito per l'app. Con una PWA spesso significava semplicemente aprire un URL.
- Hai rimosso le difficoltà legate all'installazione di un'app.
Il nostro framework
Per bollettino, abbiamo usato Polymer, ma qualsiasi modello moderno e ben supportato il modello di machine learning.
Cosa abbiamo imparato sui service worker
Non puoi avere una PWA senza un servizio worker. Service worker offrono molta potenza, ad esempio strategie avanzate di memorizzazione nella cache, funzionalità offline, sincronizzazione in background E così via. Anche se i service worker aggiungono un po' di complessità, abbiamo scoperto che i loro vantaggi superavano complessità.
Se puoi, genera
Evita di scrivere uno script del service worker a mano. La scrittura a mano dei service worker richiede l'uso manuale la gestione delle risorse memorizzate nella cache e la logica di riscrittura comuni alla maggior parte delle librerie dei Service worker, come Workbox.
Detto questo, a causa del nostro stack tecnico interno non abbiamo potuto utilizzare una libreria per generare e gestire al nostro service worker. Le informazioni apprese di seguito rispecchiano a volte questo aspetto. Vai su Insidie per Service worker non generati per ulteriori informazioni.
Non tutte le librerie sono compatibili con il service worker
Alcune librerie JS partono da ipotesi che non funzionano come previsto quando vengono eseguite da un service worker. Per
supponendo che window
o document
siano disponibili oppure utilizzando un'API non disponibile per il servizio
worker (XMLHttpRequest
, spazio di archiviazione locale ecc.). Assicurati che tutte le librerie critiche di cui hai bisogno
sono compatibili con il service worker. Per questa particolare PWA, abbiamo voluto usare
gapi.js per l'autenticazione, ma erano
non è in grado di farlo perché non supportava i service worker. Gli autori delle biblioteche devono anche ridurre o rimuovere
ipotesi inutili sul contesto JavaScript ove possibile per supportare l'uso del service worker
in altri casi, ad esempio evitando API incompatibili con i service worker ed evitando le
.
Evita di accedere a IndexedDB durante l'inizializzazione
Non leggere IndexedDB quando inizializzare lo script del service worker, altrimenti potresti riscontrare questa situazione indesiderata:
- L'utente ha un'app web con IndexedDB (IDB) versione N
- Viene eseguito il push della nuova app web con IDB versione N+1
- L'utente visita la PWA e questo attiva il download del nuovo service worker
- Il nuovo service worker legge da IDB prima di registrare il gestore di eventi
install
, attivando un Ciclo di upgrade dell'IDB per passare da N a N+1 - Poiché l'utente ha un client precedente con la versione N, il processo di upgrade del service worker si blocca come attivo sono ancora aperte alla versione precedente del database
- Il Service worker si blocca e non esegue mai l'installazione
Nel nostro caso, la cache è stata invalidata al momento dell'installazione del service worker, quindi se il service worker non ha mai gli utenti non hanno mai ricevuto l'app aggiornata.
Rendilo resiliente
Sebbene gli script dei service worker vengano eseguiti in background, possono anche essere terminati in qualsiasi momento, nel bel mezzo delle operazioni di I/O (rete, IDB e così via). Qualsiasi processo a lunga esecuzione dovrebbe ripristinabile in qualsiasi momento.
Nel caso di un processo di sincronizzazione che caricava file di grandi dimensioni sul server e salvati su IDB, la nostra soluzione per i caricamenti parziali interrotti è stato l'uso del ripristinabile dalla nostra libreria di caricamento interna salvando l'URL di caricamento ripristinabile in IDB prima di caricarlo e usandolo per ripristinare se non è stato completato la prima volta. Inoltre, prima di qualsiasi operazione di I/O a lunga esecuzione, è stato salvato nell'IDB per indicare il punto del processo in cui ci trovavamo per ogni record.
Non dipendono dallo stato globale
Poiché i service worker esistono in un contesto diverso, molti simboli che potresti aspettarti non lo sono
presenti. Gran parte del nostro codice è stato eseguito sia in un contesto window
che in un contesto di service worker (come
come logging, flag, sincronizzazione e così via). Il codice deve essere difensivo nei confronti dei servizi che utilizza, ad esempio
archiviazione locale o cookie. Puoi utilizzare
globalThis
fare riferimento all'oggetto globale in modo da funzionare in tutti i contesti. Usa anche i dati memorizzati
in variabili globali con parsimonia, in quanto non vi è alcuna garanzia su quando lo script verrà terminato
lo stato sfrattato.
Sviluppo locale
Una componente importante dei service worker è la memorizzazione nella cache delle risorse localmente. Tuttavia, durante lo sviluppo,
sia l'opposto di ciò che vuoi, in particolare quando gli aggiornamenti vengono eseguiti lentamente. Vuoi ancora
il worker del server installato, in modo da poter eseguire il debug dei problemi o utilizzare altre API come
la sincronizzazione in background o le notifiche. Su Chrome puoi farlo con Chrome DevTools
attivando la casella di controllo Bypass per la rete (riquadro Application > Service worker) in
oltre ad attivare la casella di controllo Disabilita cache nel riquadro Rete per
disattivare la cache in memoria. Per includere più browser, abbiamo optato per un'altra soluzione
incluso un flag per disabilitare la memorizzazione nella cache nel nostro service worker che è abilitato per impostazione predefinita
le build. Ciò garantisce che gli sviluppatori ricevano sempre le modifiche più recenti senza problemi di memorizzazione nella cache. È
è importante includere l'intestazione Cache-Control: no-cache
per impedire al browser di
memorizzando gli asset nella cache.
Faro
Lighthouse offre diverse funzionalità di debug utili per le PWA. Scansiona un sito e genera report relativi a PWA, rendimento accessibilità, SEO e altre best practice. Ti consigliamo di eseguire Lighthouse in modalità continua integrazione per ricevere un avviso se si interrompe una delle per essere una PWA. In realtà è successo una volta, quando il service worker non stava installando e non ce l'abbiamo realizzato prima di una spinta in produzione. Avere Lighthouse nella nostra CI avrebbe lo ha impedito.
Sfrutta la distribuzione continua
Poiché i service worker possono eseguire gli aggiornamenti automaticamente, gli utenti non hanno la possibilità di limitare gli upgrade. Questo riduce in modo significativo la quantità di clienti obsoleti. Quando l'utente ha aperto l'app, il service worker supportava il vecchio client mentre scaricava lentamente il nuovo client. Una volta scaricato un nuovo client, all'utente verrebbe chiesto di aggiornare la pagina per accedere alle nuove funzionalità. Anche se l'utente ha ignorato questa richiesta e la volta successiva che aggiornava la pagina avrebbe ricevuto la nuova all'ultima versione del client. Di conseguenza, è abbastanza difficile per un utente rifiutare gli aggiornamenti nello stesso per le app per iOS/Android.
Siamo stati in grado di eseguire il push delle modifiche al backend che provocano errori con un tempo di migrazione molto breve per clienti. In genere, concediamo un mese agli utenti per passare ai nuovi clienti prima di effettuare che interrompono le modifiche. Dal momento che l'app non funzionava, era possibile per i client meno recenti esistenti se l'utente non apre l'app da molto tempo. Su iOS, i Service worker Rimosso dopo un paio di settimane quindi questo caso non accade. Per Android, questo problema potrebbe essere mitigato se non viene pubblicato mentre vecchi o con scadenza manuale dopo alcune settimane. In pratica, non abbiamo mai riscontrato per risolvere i problemi da client inattivi. La severità di un determinato team dipende dal suo utilizzo specifico in questo caso, ma le PWA offrono una flessibilità notevolmente maggiore rispetto alle app per iOS/Android.
Recupero dei valori dei cookie in un service worker
A volte è necessario accedere ai valori dei cookie in un contesto di service worker. Nel nostro caso,
necessari per accedere ai valori dei cookie per generare un token per autenticare le richieste API proprietarie. In un
service worker, le API sincrone come document.cookies
non sono disponibili. Puoi sempre inviare un
messaggio ai client attivi (con finestre) dal service worker per richiedere i valori del cookie, tuttavia
è possibile che il service worker venga eseguito in background senza client che rientrano in una finestra
disponibili, ad esempio
durante una sincronizzazione in background. Per ovviare a questo problema, abbiamo creato un endpoint
server frontend che si limita a ricondurre il valore del cookie al client. Il service worker ha effettuato un
una richiesta di rete a questo endpoint e leggere la risposta per ottenere i valori dei cookie.
Con il rilascio API Cookie Store, questa soluzione alternativa non dovrebbe più essere necessaria per i browser che la supportano, in quanto accesso asincrono ai cookie del browser e può essere utilizzato direttamente dal service worker.
Insidie per i service worker non generati
Assicurati che lo script del service worker venga modificato se vengono apportate modifiche a un file statico memorizzato nella cache
Un pattern PWA comune per un service worker prevede l'installazione di tutti i file statici dell'applicazione durante la sua
fase install
, che consente ai client di accedere alla cache dell'API Cache Storage direttamente per tutti
le visite successive . I service worker vengono installati solo quando il browser rileva che il servizio
lo script worker è cambiato in qualche modo, quindi abbiamo dovuto assicurarci che lo stesso file di script del service worker
modificato in qualche modo quando un file memorizzato nella cache è stato modificato. Per farlo manualmente, incorporando un hash
un set di file di risorse statico all'interno del nostro script del service worker, per cui ogni release generava un
del Service worker. Librerie dei service worker come
Workbox automatizza questo processo.
Test delle unità
Le API dei service worker funzionano aggiungendo listener di eventi all'oggetto globale. Ad esempio:
self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));
Può essere complicato testare questa operazione, perché devi simulare l'attivatore di evento, l'oggetto evento,
il callback respondWith()
, quindi attendi la promessa, prima di asserire infine il risultato. Un
il modo più semplice per strutturare questo aspetto è delegare tutta l'implementazione a un altro file, che è più facile
viene testato.
import fetchHandler from './fetch_handler.js';
self.addEventListener('fetch', (evt) => evt.respondWith(fetchHandler(evt)));
A causa delle difficoltà legate al test delle unità di uno script del service worker, abbiamo mantenuto il service worker principale uno script il più semplice possibile, suddividendo la maggior parte dell'implementazione in altri moduli. Dal giorno questi file erano solo moduli JS standard, potrebbero essere più facilmente testati nelle unità con test standard librerie.
Non perderti le parti 2 e 3
Nelle parti 2 e 3 di questa serie parleremo della gestione dei media e di problemi specifici di iOS. Se Vuoi chiederci di più sulla creazione di una PWA su Google, visita i nostri profili dell'autore per scoprirlo come contattarci: