Ce que l'équipe Bulletin a appris sur les service workers lors du développement d'une PWA
Ce message est le premier d'une série d'articles de blog sur les enseignements tirés par l'équipe Google Bulletin tout en créant une PWA externe. Dans ces publications, nous partagerons certains des défis auxquels nous avons été confrontés, les approches que nous avons adoptées pour les surmonter et des conseils généraux pour éviter les pièges. C'est par non signifie une vue d'ensemble complète des PWA. L'objectif est de partager les enseignements tirés de l'expérience de notre équipe.
Pour ce premier article, nous aborderons d'abord quelques informations de base, puis nous examinerons toutes sur les service workers.
Contexte
Bulletin a été activement développé entre mi-2017 et mi-2019.
Pourquoi nous avons choisi de créer une PWA
Avant d'examiner en détail le processus de développement, voyons pourquoi la création d'une PWA était intéressante. pour ce projet:
- Capacité à effectuer des itérations rapidement : Particulièrement utile puisque Bulletin serait testé dans plusieurs marchés.
- Code base unique : Nos utilisateurs étaient répartis à peu près à parts égales entre Android et iOS. Une PWA signifiait nous pourrions créer une seule application web qui fonctionnerait sur les deux plateformes. Cela a augmenté la vitesse et de l'impact de l'équipe.
- Mises à jour rapides, indépendamment du comportement des utilisateurs : Les PWA peuvent automatiquement mettre à jour réduit le nombre de clients obsolètes dans la nature. Nous avons pu déployer la rupture du backend les modifications avec un temps de migration très court pour les clients.
- Intégration facile aux applications propriétaires et tierces Ces intégrations étaient indispensables pour l'application. Une PWA revient souvent à ouvrir une URL.
- Il n'est plus facile d'installer une application.
Notre cadre
Pour Bulletin, nous avons utilisé Polymer, mais toute version moderne et compatible le framework fonctionnera.
Ce que nous avons appris sur les service workers
Vous ne pouvez pas avoir de PWA sans service ou un nœud de calcul. Service workers des fonctionnalités avancées, telles que des stratégies avancées de mise en cache, des fonctionnalités hors connexion, la synchronisation en arrière-plan, Bien que les service workers ajoutent une certaine complexité, nous avons constaté que leurs avantages l'emportent sur les avantages supplémentaires la complexité.
Générez-le si vous le pouvez
Évitez d'écrire manuellement le script d'un service worker. L'écriture manuelle des service workers nécessite la gestion des ressources mises en cache et la logique de réécriture commune à la plupart des bibliothèques de service workers, Workbox.
Cela dit, en raison de notre pile technologique interne, nous ne pouvions pas utiliser de bibliothèque pour générer et gérer notre service worker. Les enseignements que nous en avons tirés ci-dessous peuvent parfois en tenir compte. Accédez aux pièges liés aux service workers non générés pour en savoir plus.
Toutes les bibliothèques ne sont pas compatibles avec un nœud de calcul de service
Certaines bibliothèques JavaScript reposent sur des hypothèses qui ne fonctionnent pas comme prévu lorsqu'elles sont exécutées par un service worker. Pour
en supposant que window
ou document
sont disponibles, ou que vous utilisez une API non disponible pour le service
(XMLHttpRequest
, stockage local, etc.). Assurez-vous que toutes les bibliothèques essentielles
sont compatibles avec un service worker. Pour cette PWA en particulier, nous voulions utiliser
gapi.js pour l'authentification, mais étaient
car il n'est pas compatible avec les service workers. Les auteurs de bibliothèques
doivent également réduire ou supprimer
des hypothèses inutiles sur le contexte JavaScript lorsque cela est possible pour permettre l'utilisation des service workers
dans certains cas, par exemple en évitant les API incompatibles avec un service worker et en évitant les opérations
l'état.
Éviter d'accéder à IndexedDB pendant l'initialisation
Ne lisez pas IndexedDB lorsque Initialisez le script de votre service worker. Dans le cas contraire, vous pouvez rencontrer ce problème:
- L'utilisateur dispose d'une application Web avec IndexedDB (IDB) version N
- La nouvelle application Web est publiée avec la version N+1 de l'IDB
- L'utilisateur visite la PWA, ce qui déclenche le téléchargement d'un nouveau service worker
- Le nouveau service worker lit l'IDB avant d'enregistrer le gestionnaire d'événements
install
, déclenchant ainsi une Cycle de mise à niveau de la IdP pour passer de N à N+1 - Étant donné que l'utilisateur dispose d'un ancien client avec la version N, le processus de mise à niveau d'un service worker se bloque en tant qu'actif. les connexions restent ouvertes à l'ancienne version de la base de données
- Le service worker se bloque et ne s'installe jamais
Dans notre cas, le cache a été invalidé lors de l'installation d'un service worker. Par conséquent, si le service worker n'a jamais installé, les utilisateurs n'ont jamais reçu la mise à jour de l'application.
Résilience
Bien que les scripts de service worker s'exécutent en arrière-plan, ils peuvent également être arrêtés à tout moment, même lors d'opérations d'E/S (réseau, IDB, etc.). Tout processus de longue durée doit être avec reprise à tout moment.
Dans le cas d’un processus de synchronisation qui a importé des fichiers volumineux sur le serveur et enregistré dans IDB, notre solution des importations partielles interrompues a consisté à exploiter le service avec reprise de notre bibliothèque interne enregistre l'URL d'importation avec reprise dans l'IDB avant l'importation et utilise cette URL pour reprendre une mettre en ligne s'il n'a pas terminé la première fois. Avant toute opération d'E/S de longue durée, a été enregistré dans IDB pour indiquer où nous en étions dans le processus pour chaque enregistrement.
Ne pas dépendre de l'état global
Étant donné que les service workers existent dans un contexte différent, de nombreux symboles auxquels on peut s'attendre
à l'heure actuelle. Une grande partie de notre code s'exécutait à la fois dans un contexte window
et dans un contexte de service worker (tel que
comme la journalisation, les options, la synchronisation, etc.). Le code doit être défensif vis-à-vis des services qu'il utilise, par exemple
le stockage local ou les cookies. Vous pouvez utiliser
globalThis
pour faire référence à l'objet global d'une manière qui fonctionne dans tous les contextes. Utiliser aussi les données stockées
avec parcimonie, car il n'y a aucune garantie quant au moment où le script sera arrêté et
l'État évincé.
Développement local
L'un des composants majeurs des service workers consiste à mettre en cache les ressources localement. Cependant, au cours du développement,
est exactement l'inverse de ce que vous souhaitez, en particulier lorsque les mises à jour sont effectuées de manière différée. Vous souhaitez toujours
le serveur worker installé afin que vous puissiez le déboguer
ou travailler avec d'autres API comme
la synchronisation en arrière-plan
ou les notifications. Pour cela, utilisez les outils pour les développeurs Chrome :
en cochant la case Ignorer pour le réseau (panneau Application > volet Service workers,
cochez la case Désactiver le cache dans le panneau Réseau pour que
désactiver le cache de la mémoire. Afin de couvrir davantage de navigateurs, nous avons opté pour une autre solution en
avec un indicateur permettant de désactiver la mise en cache dans notre service worker, qui est activé par défaut sur les comptes de développement
compilations. Les développeurs bénéficient ainsi toujours de leurs modifications les plus récentes, sans problème de mise en cache. Il est
important d'inclure également l'en-tête Cache-Control: no-cache
pour empêcher le navigateur
et mettre en cache des éléments.
Phare
Lighthouse propose différents outils de débogage utiles pour les PWA. Il analyse un site et génère des rapports sur les PWA, les performances, l'accessibilité, le SEO et d'autres bonnes pratiques. Nous vous recommandons d'exécuter Lighthouse en continu intégration pour vous avertir si vous ne respectez pas l'un des pour devenir une PWA. Cela nous est arrivé une fois, lorsque le service worker n'installait pas et nous ne l’avions pas réalisé avant de passer en production. Le fait d'avoir Lighthouse dans notre intégration continue aurait l'ont empêché.
Adopter la livraison continue
Comme les service workers peuvent se mettre à jour automatiquement, les utilisateurs ne peuvent pas limiter les mises à niveau. Ce permet de réduire considérablement le nombre de clients obsolètes. Lorsque l'utilisateur a ouvert notre application, le service worker diffuserait l'ancien client alors qu'il téléchargeait le nouveau client en différé. Une fois que téléchargé un nouveau client, il serait invité à actualiser la page pour accéder aux nouvelles fonctionnalités. Même si l'utilisateur a ignoré cette demande. La prochaine fois qu'il actualiserait la page, il recevrait le nouveau du client. Il est donc assez difficile pour un utilisateur de refuser les mises à jour pour les applications iOS/Android.
Nous avons pu déployer des modifications destructives du backend en un temps de migration très court pour clients. En règle générale, nous accordons un mois aux utilisateurs pour effectuer une mise à jour vers les nouveaux clients avant de faire des modifications destructives. Étant donné que l'application était diffusée alors qu'elle n'était pas actualisée, il était possible pour les clients plus anciens d’exister dans la nature si l’utilisateur n’avait pas ouvert l’application depuis longtemps. Sur iOS, les service workers sont évincée au bout de quelques semaines donc ce cas n'arrive pas. Pour Android, ce problème pourrait être atténué en ne diffusant pas qu'ils soient obsolètes ou qu'ils arrivent à expiration manuellement au bout de quelques semaines. En pratique, nous n'avons jamais rencontré des clients obsolètes. Le niveau de rigueur d'une équipe donnée dépend de son utilisation spécifique. mais les PWA offrent beaucoup plus de flexibilité que les applications iOS/Android.
Obtenir les valeurs des cookies dans un service worker
Il est parfois nécessaire d'accéder aux valeurs des cookies dans le contexte d'un service worker. Dans notre cas, nous
pour accéder aux valeurs des cookies afin de générer un jeton permettant d'authentifier les requêtes API propriétaires. Dans un
les API synchrones telles que document.cookies
ne sont pas disponibles. Vous pouvez toujours envoyer un
un message du service worker aux clients actifs (en mode fenêtré) pour demander les valeurs des cookies,
le service worker peut s'exécuter en arrière-plan sans passer par des clients
disponibles, par exemple lors d'une synchronisation en arrière-plan. Pour contourner ce problème, nous avons créé un point de terminaison
frontend qui renvoie simplement la valeur du cookie au client. Le service worker a effectué
la requête réseau à ce point de terminaison
et lire la réponse pour obtenir les valeurs des cookies.
Avec le lancement API Cookie Store cette solution de contournement ne devrait plus être nécessaire pour les navigateurs compatibles, car elle fournit un accès asynchrone aux cookies du navigateur et peut être utilisé directement par le service worker.
Pièges pour les service workers non générés
Assurez-vous que le script de service worker change en cas de modification d'un fichier statique mis en cache
Un modèle de PWA courant consiste à installer tous les fichiers d'application statiques
La phase install
, qui permet aux clients d'appeler directement le cache de l'API Cache Storage pour tous
visites suivantes . Les service workers sont installés uniquement lorsque le navigateur détecte que le service
Le script n'a pas changé. Nous avons donc dû nous assurer que le fichier de script du service worker lui-même
modifié d’une manière ou d’une autre
lorsqu’un fichier mis en cache a été modifié. Nous l'avons fait manuellement en intégrant un hachage
de fichiers de ressources statiques dans le script Service Worker. Ainsi, chaque version produit un ensemble distinct
fichier JavaScript de service worker. Les bibliothèques de service workers telles que
Workbox automatise ce processus.
Tests unitaires
Les API Service Workers fonctionnent en ajoutant des écouteurs d'événements à l'objet global. Exemple :
self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));
Cela peut être pénible à tester, car vous devez simuler le déclencheur d'événement, l'objet événement, attendre
le rappel respondWith()
, puis attendez la promesse avant d'effectuer une assertion sur le résultat. Une
la plus simple consiste à déléguer toute l'implémentation dans un autre fichier, ce qui est plus facile.
sont testées.
import fetchHandler from './fetch_handler.js';
self.addEventListener('fetch', (evt) => evt.respondWith(fetchHandler(evt)));
Comme il était difficile de réaliser des tests unitaires sur un script de service worker, nous avons conservé le service worker principal le script le plus simple possible, en divisant la majeure partie de l'implémentation en d'autres modules. Depuis ces fichiers n'étaient que des modules JS standards, ils peuvent donc être plus facilement testés bibliothèques.
Restez à l'écoute pour les parties 2 et 3
Dans les parties 2 et 3 de cette série, nous aborderons la gestion multimédia et les problèmes spécifiques à iOS. Si vous si vous souhaitez nous en savoir plus sur la création d'une PWA chez Google, consultez nos profils d'auteurs comment nous contacter: