Blazor - Construire une PWA avec Blazor WebAssembly
J'ai eu la magnifique opportunité de présenter une session durant la BlazorDay online conference à propos de Construire une PWA avec Blazor WebAssembly
(L'enregistrement est disponible sur Youtube)
Si vous préférez un format écrit, sous forme d'un court article de blog, plutôt que de regarder une vidéo de 30 minutes, vous êtes au bon endroit.
Workbox ou ne pas Workbox
tl;dr: Vous pouvez dire au revoir à Workbox :)
Comme vous avez pu le remarquer (ou non), cet article pourrait être considéré en partie comme une mise à jour d'un ancien article Blazor - partie 4 : Comment transformer une application Blazor en PWA avec Workbox.
À ce moment-là, le fonctionnement en mode PWA avec Blazor n'était pas supporté par défaut, et configurer le tout manuellement n'était pas ce qu'il y avait de plus trivial. De ce fait, utiliser un outil aussi reconnu et éprouvé que Workbox
pour simplifier la mise en place d'une PWA semblait être une bonne solution.
Mais nous parlons d'un temps déjà très ancien ! (En fait nous parlons de seulement 8 mois plus tôt pour être plus exact haha) et à date, Blazor WebAssembly
est maintenant officiellement disponible avec la version du .NET Core SDK 3.1.300+.
Au départ, je pensais que le support du mode PWA avec Blazor serait une implémentation trop simpliste, et aurait besoin d'être rapidement remplacé par un outillage plus robuste tel que Workbox
... Mais j'ai très rapidement abandonné cette idée quand j'ai pu constater par moi-même le très bon travail fourni par l'équipe Blazor
sur le sujet, qui fournit un système prêt à l'emploi, et facilement extensible !
Cependant, comme je reste convaincu que le sujet n'est pas toujours trivial (notamment la partie concernant le service-worker
), voyons de plus près la magie opérée derrière la commande dotnet new blazorwasm -o MyNewProject --pwa
(ou derrière la case à cocher Progressive Web Application
qui se trouve dans Visual Studio)
Rendre son application installable
Pour rendre son application installable, rien d'extraordinaire de ce côté. Vous pouvez jeter un œil aux fichiers ajoutés et modifiés pour rendre votre application Blazor
installable dans ce commit Github
N'oubliez également pas d'enregistrer un service worker, même vide, pour que votre application soit considérée comme une PWA aux yeux de lighthouse.
Pour plus d'information concernant tous les configurations possibles du manifest, n'hésitez pas à jeter un coup d’œil à la documentation officielle https://developer.mozilla.org/en-US/docs/Web/Manifest.
Ajout du support offline grâce au service-worker.js
Ok, les choses commencent à devenir intéressante à partir d'ici. Pour le support de l'offline, vous avez grosso modo besoin de 2 choses :
- La liste de tous les assets à rajouter au cache du navigateur
- Une description de comment vous souhaitez gérer l'utilisation et la mise à jour du cache de votre navigateur.
Pour ce premier point, Blazor
fournit une propriété MSBuild appelée <ServiceWorkerAssetsManifest>
qui va se charger de lister tous les fichiers publiés et produire un hash
pour savoir si chaque fichier a changé ou non, pour enfin résumer toutes ces informations dans un fichier appelé service-worker-assets.js
. (Plus de détail dans ce commit Github)
Pour la seconde partie, Blazor
fournit un fichier additionnel appelé service-worker.published.js
qui contient la réelle implémentation de notre stratégie de gestion du cache, par défaut une stratégie de Cache, falling back to network
.
*Schéma de la stratégie Cache, falling back to network provenant de developers.google.com/web/fundamentals
Cependant, et comme décrit dans la documentation officielle de Microsot, le support offline n'est "activé" que lorsque l'application est "publiée", ce qui signifie que le fichier service-worker.js
n'est remplacé par le fichier service-worker.published.js
que lorsque l'on utilise la commande dotnet publish
(ou lorsque l'on publie depuis Visual Studio)
Et voilà, félicitations, votre application publiée fonctionne même en étant offline
. Mais comme toujours, j'aime bien savoir ce qui se cache réellement derrière (pour pouvoir s'en servir / le faire évoluer correctement), voyons dans dans la prochaine partie plus en détail le contenu de ce service worker.
Les événements du cycle de vie du Service worker
Si l'on regarde le contenu du fichier service-worker.published.js
, on peut y constater un workflow assez simple, fait en 4 parties :
- Une référence
self.assetsManifest
vers le manifest des assets, contenant la liste des fichiers qui ont besoin d'être mis en cache, ainsi qu'une version calculée à partir de tous les hash des fichiers, - Une action pour l'événement
installing
d'installation du service worker, - Une action pour l’événement
activating
de son activation, - Et enfin une action pour l'événement
fetching
déclenché à chaque récupération d'une ressource.
En premier lieu, l'événement installing
est appelé à chaque fois qu'une nouvelle version de la PWA est publiée, et permet la mise en cache de tous les assets décrits dans le manifest, dans un cache nommé suivant le numéro de version de l'application (calculée donc à partir de tous les hash des assets). Une fois l'installation terminée, la nouvelle version du service worker passe dans un état waiting
, attendant que toutes les instances de la PWA soit fermées pour activer la nouvelle version.
Durant le processus d'activation (événement activating
), le "nouveau" service worker devient la version "courante", et tous les caches correspondant à d'autres versions antérieures sont supprimés.
Enfin, à chaque fois qu'une ressource est demandée (telle qu'une image, une icone, un fichier html, javascript, etc.), l'événement fetch
est déclenché et permet de vérifier si la ressource est présente dans le cache. Si elle l'est, la version en cache est retournée, aucune requête réseau (nécessitant une connexion internet par exemple) n'est déclenchée, et sinon, la requête http a lieu normalement.
Bon, malgré toutes ces explications, le sujet est peut-être encore un peu obscur, et je ne saurais que vous conseiller de "déboguer" en pas à pas par vous même ce service worker pour vous familiariser avec son fonctionnement.
Pour aller plus loin
La mise à jour d'un service worker est souvent de premier abord assez surprenante (le fait de devoir attendre que toutes les versions soient fermées), et l'on peut se demander s'il est possible de "notifier" l'utilisateur de la présence d'une nouvelle version, et lui donner la possibilité de "forcer une mise à jour immédiatement". Pour cela, on peut par exemple utiliser l'instruction self.skipWaiting()
, ainsi qu'un canal de communication tel que l'API BroadcastChannel
pour communiquer entre la PWA et le Service worker (Vous pourrez trouver un exemple sur mon Github)
Ceci étant dit, j'espère que cet article aura pu vous rafraîchir la mémoire si vous ne vous souvenez plus très bien de la session, ou vous aura permis de mieux appréhender ce sujet si vous préférez un format écrit que vidéo !
Comme toujours, n'hésitez pas à me contacter sur Twitter @vivienfabing ou dans les commentaires, et que le code soit avec vous !
Commentaires