Polymer et les Progressive Web Apps (Seconde Partie)

Nous avons vu dans le premier article sur Polymer que ce framework simplifie considérablement le développement de composants réutilisables. On pourrait s’étaler longuement sur son fonctionnement, en parlant de manière plus détaillée du binding, de la gestion des événements, de la gestion du DOM, … On pourrait également présenter en détail la panoplie très fournie de composants sur étagère, le catalogue, qui vont des éléments en material design aux animations en passant par les briques de construction de sites de e-commerce. Sans compter ceux qu’on peut trouver ailleurs sur le Net, nous allons donc faire différemment …

Les Progressive Web Apps

Je préfère dans cet article vous parler des Progressive Web Apps, une nouvelle génération d’applications Web, dont l’objectif est de donner au navigateur mobile la possibilité de délivrer une User Experience digne d’une application mobile native. Nous verrons bien sûr quel est le rapport avec Polymer, et là encore, pourquoi ce framework est à l’avant garde sur le sujet.

Une application Web peut être qualifiée de Progressive Web App si elle possède les caractéristiques suivantes :

  • Responsive : elle cible le mobile, mais doit fonctionner quelque soit le support
  • Installable : on doit pouvoir l’installer sur son écran d’accueil, exactement comme avec une application téléchargée sur le Store
  • Rapide : elle doit délivrer une expérience tendant vers le natif, et pouvoir supporter les aléas du réseau mobile, voire les déconnexions
  • Supporter le push : et ce même si le navigateur est fermé
  • Sécurisée

(Le tout, bien évidemment, en utilisant des standards !)

Le critère le plus délicat à adresser, c’est la gestion des aléas du réseau et des déconnexions, inhérentes aux terminaux mobiles. En d’autres termes, être capable de gérer le offline, ou du moins de le gérer avec une graceful degradation. Pour cela, une nouveau standard est né, que beaucoup considèrent comme une évolution majeure depuis XHRHttpRequest et Ajax : les Service Worker .

Le Service Worker, c’est en quelque sorte un serveur proxy qui se place entre le site Web et le navigateur ou le réseau, et qui peut intercepter et modifier les requêtes réseau (GET et POST), et prendre les actions appropriées selon que le réseau est disponible ou pas, ou que le serveur a mis à jour des ressources.

Vous l’aurez compris, à partir de là, on peut gérer la mise en cache des ressources, qu’elles soient statiques (images, scripts, CSS,…) ou dynamiques (résultats d’appels de backend par exemple).

L’avantage des Service Worker, c’est qu’ils sont par nature sécurisés (ils ne fonctionneront que sur HTTPS, ou alors en HTTP mais sur le localhost uniquement, pour le développement), et qu’ils fonctionnent en arrière plan, même si l’onglet du navigateur est fermé (du moment que le processus reste actif, ce qui est le cas sur les OS mobiles). Le Service Worker survit même à une fermeture complète : il est réactivé à l’ouverture du navigateur.

Ils sont également à la base du fonctionnement du push, dont les principes sont définis par un DRAFT W3C, le Push API, et donc avec un avantage significatif sur les Web Sockets puisque fonctionnant en arrière plan.

Polymer et les Service Worker

Quel rapport avec Polymer allez-vous me dire ? Polymer va largement simplifier l’utilisation des Service Worker, et nous permettre de développer des applications Web qui supportent les dégradations du réseau et fonctionnent mieux en offline : une première étape aux Progressive Web Apps.

Pour utiliser les Service Worker, il faut aller chercher dans le catalogue des composants Polymer dans la catégorie Platinium. Les composants platinum-sw-* vont fournir toute la mécanique pour installer et activer le Service Worker sur notre site.

Mettons cela en pratique sur un exemple simple : un composant Polymer qui charge l’image d’un groupe de musique depuis l’API de Spotify, et qui gère sa mise en cache. Nous observerons avec les outils développeur de Google Chrome le chargement des ressources, pour bien comprendre ce qui se passe.

Commençons par installer notre environnement : d’abord NodeJS, puis Git, et enfin polymer-cli, une ligne de commande Polymer qui accélère le démarrage de projets Polymer, et fournit un serveur de développement.

D’abord nous allons créer une application vide dans notre répertoire de travail, en utilisant la ligne de commande polymer :

Sélectionner application, et lui donner comme nom polymer-sw par exemple. Puis, nous mettons à jour le fichier bower.json pour ajouter les dépendances sur les composants Polymer que nous allons utiliser : platinium pour les Service Worker, paper pour les éléments d’UI, et iron pour les appels Ajax.

Lancer ensuite un bower install pour qu’il télécharge les dépendances. Pour lancer un serveur de test, il suffit de lancer dans le répertoire de l’application la commande polymer serve.

Cacher les ressources statiques

Nous allons commencer par illustrer la mise en cache des ressources statiques. Dans le fichier html polymer-sw-app.html, commençons comme suit :

Il n’y a rien de particulier dans ce composant, si ce n’est l’initialisation des Service Worker, avec les balises platinium suivantes :

  • platinium-sw-register, qui enregistre et active le Service Worker au chargement de la page
  • platinum-sw-cache, qui permet de pré-cacher automatiquement les ressources. Il s’appuie sur la librairie sw-toolbox, dont il masque la complexité (cet élément, comme les autres de la catégorie platinium, doit être sous platinium-sw-register)

La magie opère simplement et rapidement. Pour cela, ouvrons Chrome en mode navigation privée (préférable pour reproduire un scénario de test sans tenir compte des tests précédents et du cache). Au premier chargement de la page dans le navigateur, un Service Worker est enregistré, et toutes les ressources statiques appelées par la page (et la page elle-même) sont mises en cache, comme on peut le voir dans l’onglet Resources des outils développeur de Google.

Capture1

 

Capture2

Notez le nom du cache : il dépend du site, et il est préfixé par un nom qu’il est possible de paramétrer.

Si on regarde maintenant dans l’onglet Network des outils développeur, avec l’option Preserve Log, pour voir l’enchaînement des appels :

Capture4

On voit que d’abord les ressources sont téléchargées normalement. Ensuite, le Service Worker est enregistré et on voit que le localhost est rechargé, cette fois-ci à l’initiative du Service Worker (from Service Worker). Ensuite, toutes les ressources sont chargées directement dans le cache par le Service Worker (petit icône “outil” à côté du nom de la ressource).

Si on vide la log Network et on recharge la page avec F5 :

Capture5

C’est le Service Worker qui prend la main dès le départ, et va recharger les ressources dans le cache.

Si on arrête le serveur, et qu’on fait F5, ça fonctionne encore !!! Le Service Worker, toujours enregistré et actif, renvoie les ressources du cache, avec préalablement, une tentative de récupération des ressources sur le serveur. Ce comportement dépend de la stratégie de cache, par défaut à networkFirst, mais que l’on peut définir à cacheFirst ou cacheOnly. De même, les composants platinium permettent de définir quelles sont les ressources que l’on souhaite mettre en cache.

Plus fort encore : si on ferme le navigateur (tous les processus Chrome doivent être arrêtés) et qu’on relance, ça fonctionne encore !!! Le Service Worker survit !

Intercepter des appels et cacher le résultat

Nous allons maintenant modifier le composant polymer-sw-app pour illustrer l’interception de requêtes, et la mise en cache de résultats, ici les résultats des appels à l’API Spotify et l’image téléchargée. Pour cela, on commence par ajouter dans le composant un bouton qui réalisera un appel Ajax à l’API, ainsi qu’une zone qui affichera le résultat.

Avant cela il faut intercepter l’appel à Spotify. Nous ajoutons dans la balise polymer-sw-register les balises suivantes :

La balise platinum-sw-import-script permet d’importer le script qui va traiter l’interception des requêtes, tandis que la balise platinum-sw-fetch va déterminer quels patterns d’URL intercepter, et quelle fonction Javascript va traiter l’interception. L’attribut origin permet d’autoriser les cross-site requests (entre localhost et spotify.com).

Le code du handler est assez simple :

La requête est interceptée, et l’API CacheStorage est utilisé pour mettre en cache le résultat et le servir. Ensuite, dans le composant, nous ajoutons tout le reste, ce qui donne au final :

Le composant iron-ajax effectue l’appel à Spotify, qui est intercepté, et le résultat JSON est mis en cache par le Service Worker. Quand l’image est affichée, un GET HTTP a lieu, et elle est également automatiquement mise en cache, comme l’attestent les captures suivantes :

Capture6 Capture7

La gestion de l’évènement ready permet de gérer l’affichage de l’image en cache lors du refresh.

La puissance des Service Worker

Cet exercice vous a présenté une utilisation de base des Service Worker. Mais on peut aller beaucoup, beaucoup plus loin…

L’interception et la modification des requêtes HTTP permettent de mettre en place côté client des patterns sophistiqués, jusqu’alors impossibles à faire via le navigateur :

  • La détection du offline, de manière fiable et permettant d’adapter le comportement (fallback, message d’erreur, …)
  • Du load balancing : déléguer au Service Worker la récupération des ressources sur le meilleur fournisseur de contenu disponible
  • Un mock serveur côté client : demander au Service Worker d’intercepter et de répondre lui-même aux appels de ressources serveur pour simuler un backend et tester une UI
  • Un cache de ressource chargé depuis un fichier ZIP
  • Un buffer de requêtes : le Service Worker empile les requêtes dès que le réseau est coupé, et les dépile dès que le réseau devient disponible

Ce site de Mozilla décrit plusieurs usages avancés, notamment ceux liés au push.

Polymer et les Progressive Web Apps

Au delà des Service Worker, Polymer est à l’heure actuelle le framework le plus complet pour développer des Progressive Web Apps :

  • Il gère déjà très bien le responsive
  • Il fournit des composants de type AppShellun squelette minimal faisant tourner le site, que l’on met systématiquement en cache pour un chargement instantané
  • Il fournit également les composants pour gérer le push (pour le moment uniquement via le service de push de Google)

Ce codelab de Google vous donnera une vision complète de ce qu’est une Progressive Web App avec Polymer.

Pour conclure…

Comme pour Polymer, le support est relativement limité : Chrome, Firefox et Opera, ainsi que la dernière version du navigateur Android. Rien sur IOS, rien sur Windows Phone. On est donc encore loin de pouvoir utiliser les Service Worker, le Push API, et globalement les Progressive Web Apps sur tous les supports mobiles en production. Mais nul doute qu’étant en DRAFT W3C, tous ces standards finiront un jour ou l’autre par être entérinés puis supportés par tous les éditeurs de navigateurs, et nous serons alors à même de délivrer une User Experience optimale sur les sites Web mobiles.

Leave a Reply

Your email address will not be published. Required fields are marked *