De retour pour un nouvel article sur un sujet à la mode : les Progressive Web Apps ! Cela fait quelques mois que j’ai envie d’écrire sur cette technologie que je trouve très intéressante et pertinente à l’heure actuelle. Les Progressive Web Apps, que je vais abréger par PWA dans la suite de l’article, sont nées en 2015 sur l’impulsion de Frances Berriman et de l’ingénieur de chez Google Alex Russell. Elles apportent des réponses à plusieurs constats et problématiques récents :

  • Le nombre d’utilisateurs naviguant sur le web via un périphérique mobile (smartphone ou tablette) est en constante augmentation. Depuis octobre 2016, il dépasse même le nombre de personnes utilisant un desktop (source StatCounter), en particulier dans certains marchés (Afrique et Asie).
  • Les utilisateurs téléchargent toujours plus d’applications… (environ 90 apps installées).
  • … mais par contre, ils n’en utilisent finalement que très peu : une trentaine est utilisée par mois et seulement 9 de façon journalière (source App Annie).
  • Ces applications sont de plus en plus lourdes et occupent un espace non négligeable sur nos smartphones (c’est surtout valable dans les marchés émergents).
  • Les stores d’applications sont saturés !

Je vais commencer par brièvement lister les différentes façons d’adresser le marché du mobile :

Description Avantages Inconvénients
Site web mobile Site distinct et dédié à la consultation via une tablette/smartphone (via une détection auto) Permet de proposer du contenu et une expérience spécifique et adaptée à une audience mobile (indépendamment du site principal)Le contenu (images…) et le temps de chargement sont optimisés Pas de mode hors-connexionL’accès aux fonctionnalités du mobile est rare (appareil photo, NFC…)Cohérence entre le site principal et le site mobile (et double maintenance !)
Site web responsive Un unique site qui adapte sa disposition en fonction du périphérique de consultation Pas besoin de développer un site spécifique, mais « simplement » d’adapter l’existantURL uniqueConservation du référencement naturel et de l’indexation Il faut généralement le prévoir en amont et cela demande du tempsLes contraintes techniques peuvent être importantes (on ne peut pas faire tout ce que l’on veut)
Application mobile native Application développée spécifiquement pour un OS mobile (iOS, Android…) et téléchargeable via un store Excellente expérience utilisateurRéférencement et visibilité optimaux (via les stores)Utilisation de 100% des ressources matérielles et logicielles du périphériques (= performances)Design cohérent par rapport à la plateformePossibilité de monétiser l’app Utilisation hors-ligne Coût du développement et de la maintenance (3 OS/3 langages/3 process)Difficulté de monter une équipe quand on cible plusieurs OS
Application mobile hybride Mix entre l’application web et l’application native (Cordova, Ionic, ReactNative, Xamarin…) Communauté importante et dynamiqueRéutilisation des compétences des équipes web (HTML/CSS/JS) Performance encore en retrait à cause de l’utilisation d’une WebView (même si l’écart se réduit)UI « moins riche »Dépendance aux plugins pour profiter des fonctionnalités du périphérique

Qu’est-ce qu’une Progressive Web App ?

On peut définir une PWA comme une application pouvant accéder aux fonctionnalités natives du périphérique mais basée sur des technologies web. Elles sont définies par plusieurs caractéristiques :

  • **Rapide et instantanée : **la possibilité d’exécuter du code Javascript en arrière-plan (via les services Worker) permet de maintenir en permanence l’application à jour et d’offrir à l’utilisateur les dernières données dès qu’il ouvre l’application. L’utilisation des dernières techniques d’animation CSS permet d’obtenir un rendu aussi fluide que pour une application native.
  • Légère : la taille de l’application et l’empreinte mémoire sont faibles, ce qui est très intéressant pour les pays en voie de développement notamment ou les petits forfaits mobile. Seul le contenu nécessaire est chargé.
  • Indépendante du réseau : via les mécanismes de cache, l’accès au réseau devient optionnel et une utilisation hors-ligne est possible (très pratique dans les pays émergents ou en situation de mobilité).
  • Responsive : smartphone, tablette, desktop, montre connectée… l’application doit suivre l’utilisateur et s’adapter au périphérique utilisé (et tout cela en plein écran pour donner l’illusion la plus parfaite).
  • Sécurisée : HTTPS obligatoire !
  • Engageante: l’engagement de l’utilisateur doit-être facilité via des fonctionnalités comme les notifications push (et ce, même si l’application est fermée).
  • Découvrable : elle doit pouvoir être indexée par les moteurs de recherche et reconnue comme une application (via le manifest). Cela permet de profiter des techniques bien connues de référencement du web : publicité, achat de liens sponsorisés…
  • Installable : accessible via un icône sur l’écran d’accueil (comme une application native !) mais sans passer par les étapes d’accès au store, de téléchargement et d’installation. On perd environ 20% des utilisateurs potentiels à chacune de ces étapes, donc moins d’étapes c’est moins de perte !
  • Facile à partager : pas de soucis, grâce à une simple URL ! Cela permet de diffuser facilement sur plusieurs canaux : réseaux sociaux, annuaires, mails…

Un point mérite, pour moi, d’être particulièrement mis en évidence : l’absence de store. Cela permet plusieurs choses :

  • Pas de droits d’entrée à payer pour s’inscrire sur le store(25$ pour Google Play et 99$ par an pour l’AppStore)
  • Pas de frais de commission (~30%)
  • Pas de délai de validation pour la publication (1ère fois et mise à jour) ce qui permet d’être beaucoup plus réactif
  • Plus de soucis par rapport à la politique du store (Apple est connu pour refuser des applications sous des justifications parfois fumeuses…)
  • Par contre, on ne peut pas profiter de la visibilité et de la mise en avant proposées par le store

Les composants

Composants d'une PWA (source : https://fr.goodbarber.com) Composants d'une PWA (source : https://fr.goodbarber.com)

Une PWA est principalement composée de 3 éléments :

  • L’AppShell
  • Un Service Worker
  • Un fichier Manifest

L’App Shell

App Shell (source : https://developers.google.com/web/fundamentals/architecture/app-shell) App Shell (source : https://developers.google.com/web/fundamentals/architecture/app-shell)

Il représente le squelette de l’application et est composé uniquement des éléments indispensables à son fonctionnement de base (mécanisme de navigation, menu…). C’est donc une sorte de coquille vide (HTML, JS, CSS, images, contenus statiques…) qui sera mise en cache lors de la 1ère visite pour éviter d’être téléchargée à chaque fois et accélérer la navigation. Ensuite, seules les données seront récupérées pour venir s’insérer dans la structure. Cela permet de forcer la séparation du contenu par rapport à la structure.

Le Service Worker

C’est le principal élément distinguant une PWA d’un site web “classique”. Techniquement, c’est simplement un script JS exécuté en arrière-plan par le navigateur. Il écoute et permet de réagir à des évènements. C’est lui qui permet de faire fonctionner les notifications push et la gestion du cache (et donc du mode hors-ligne). Il agit comme une sorte de proxy entre notre application et le web. Le point important est que le Service Worker est capable d’effectuer des tâches même si l’utilisateur n’est pas sur l’application ou que celle-ci n’est pas ouverte.

Cycle de vie :

Service Worker Lifecycle (https://developers.google.com/web/fundamentals/primers/service-workers/) Service Worker Lifecycle (https://developers.google.com/web/fundamentals/primers/service-workers/)

Quelques points d’attention à propos du service worker :

  • Pour l’installer, il faut qu’il soit enregistré : navigator.serviceWorker.register(‘service-worker.js’)
  • Il est complétement décorrélé de la page web (thread séparé).
  • Étant dans un thread dédié en arrière-plan (Web Worker), il n’a pas d’accès direct au DOM. La communication va donc se faire par échange de messages via postMessage.
  • Comme vu plus haut, le HTTPS est obligatoire, la seule exception étant localhost pour simplifier les développements.
  • Il fonctionne en mode réactif, donc une fois la tâche terminée, il passe en mode “repos” et ne consomme plus de ressources (ou très peu :)) jusqu’à la prochaine action.

Il est possible d’avoir une gestion assez fine sur les différents évènements du cycle de vie d’un service worker, et notamment pour gérer sa mise à jour. Je vous redirige pour cela vers cet excellent article.

Le Manifest JSON (Web App Manifest)

C’est l’élément clé permettant aux navigateurs de savoir comment afficher et traiter notre PWA. C’est aussi grâce à lui que l’on va pouvoir permettre son “installation” sur l’écran d’accueil du smartphone. Il permet d’indiquer les métadonnées suivantes :

  • Nom et description
  • Couleur principale
  • Icônes
  • Url de démarrage
  • Orientation par défaut
  • Type d’affichage
  • Langue
  • Splashscreen

Pour une couverture complète de l’ensemble des possibilités, je vous redirige vers la documentation de Mozilla. Pour mieux comprendre, voici un exemple :

{
  "name": "pwa-sample",
  "short_name": "pwa-sample",
  "icons": [
    {
      "src": "/static/img/mySmallIcon.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/static/img/myBigIcon.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#000000",
  "theme_color": "#4DBA87"
}

L’extension officielle telle que décrite dans la norme est .webmanifest (https://w3c.github.io/manifest/#media-type-registration) mais les navigateurs supportent très bien le format JSON. Pour être visible par le navigateur, il doit-être référencé dans la page HTML via la balise suivante : .

L’installation de l’application sur l’écran d’accueil est forcément à l’initiative de l’utilisateur, soit directement à partir du menu du navigateur (cf. captures d’écran ci-dessous), soit via une bannière d’installation. Attention par contre, l’affichage de cette bannière n’est pas à la charge du développeur, c’est automatique et décidé par le navigateur. Chrome par exemple respecte les critères suivants :

  • La PWA n’est pas déjà installée
  • L’utilisateur montre de l’engagement (il a interagi avec le site pendant au moins 30 secondes)
  • Un fichier manifest est présent et au bon format
  • Le site est en HTTPS
  • Un service worker est enregistré

Si ces critères sont respectés, alors Chrome va lancer l’évènement “beforeinstallprompt” pour déclencher l’affichage de la bannière d’installation. En fonction du navigateur, les critères ne sont pas exactement les mêmes : Firefox, Edge et Opera.

Une fois l’application installée, la question de la mise à jour du manifest devient importante. Malheureusement, cette fonctionnalité n’est pas supportée et il n’existe pas encore de manière de forcer la mise à jour de ce fichier. L’utilisateur est donc obligé de supprimer et ré-ajouter la PWA… Des discussions sont en cours pour pallier à ce manque.

Compatibilité

La force numéro 1 des PWA est leur excellente compatibilité multi-plateformes ainsi que leur adaptabilité. Malheureusement, tout n’est pas encore parfait et il reste quelques irréductibles (qui a parlé d’Apple ?). Je vous invite à consulter ce lien qui détaille avec une grande précision le support des fonctionnalités des PWA par navigateurs et systèmes d’exploitation.

 Can I use - Web App Manifest (https://caniuse.com/#search=web%20app%20manifest) Can I use - Web App Manifest (https://caniuse.com/#search=web%20app%20manifest)

Can I use n’est apparemment pas à jour pour Firefox car celui-ci supporte bien la fonctionnalité depuis la version 58 (https://platform-status.mozilla.org/#app-manifest). Côté Safari, c’est enfin disponible sur iOS (depuis Mars 2018 !) mais de manière incomplète. Côté Mac, rien pour le moment, mais ça ne saurait tarder, c’est en cours de développement.

Plus de détails sur le support dans cet article très complet.

A noter, le Microsoft Store de Windows commence à accueillir depuis quelques mois des applications PWA ! On peut citer Uber et Twitter. Celles-ci sont disponibles à partir de la build 1803 (“Springs Fall Creators”). C’est une excellente nouvelle pour enrichir la visibilité de vos PWA.

Quelques outils

Une PWA de qualité passe évidemment par l’utilisation d’outils de développement adaptés :

  • Les outils de développement de Google Chrome évidemment ! Ils permettent d’inspecter le manifest, les services workers, le contenu du cache…

  • L’extension LightHouse de Chrome permet de dresser un rapport sur la qualité et la performance d’une application web ainsi que sa conformité aux standards PWA.
Chrome - LightHouse Chrome - LightHouse

Quelques PWA…

De nombreux sites ont tentés l’expérience des PWA, avec globalement des retours très positifs :

  • Twitter avec Twitter Lite. Avec environ 300 millions d’utilisateurs mensuels et 80% de cette audience à partir d’un mobile, Twitter avait tout intérêt à lorgner du côté des PWA. Résultat, celle-ci est 30% plus rapide et consomme 70% de données en moins par rapport à l’app standard, et tout cela pour un espace occupé sur le smartphone de moins d’un Mo (source)
  • Lancôme souhaitait améliorer son taux de conversion suite à une utilisation de plus en plus mobile de son site (source).
  • L’équipe : avec une audience de 2,6 millions de visiteurs dont 1,6 à partir d’un mobile, L’Equipe est le 1er média français à adopter la PWA.
  • De nombreux médias US : The Washington Post (source), Forbes, Financial Times…
  • De nombreux autres sites :
    • Alibaba (source)
    • AliExpress (source)
    • Starbucks
    • Pinterest
    • Uber
    • Telegram
    • Flipboard
    • Google Maps Go

Exemple

Pour illustrer la théorie abordée dans les précédents paragraphes, j’ai décidé de réaliser une petite PWA de démonstration. Pour cela, je me suis appuyé sur une API listant les bières Brewdog’s avec leurs caractéristiques. Pour simplifier la mise en place, j’ai utilisé un template permettant de faire une PWA avec le framework VueJS. Voici le lien vers le repository GitHub correspondant.

Vue.js

Ce framework a été créé en 2014. Reposant sur le pattern MVVM, il s’inspire en partie d’AngularJS et se base sur des mécanismes propres à React (le DOM virtuel par exemple). Par rapport à ces deux frameworks, Vue est très léger (environ 23 Ko), performant et simple à appréhender. Sans trop rentrer dans le détail, ce framework apporte quelques avantages très intéressants :

  • Une approche fortement orientée composant (mais non imposée)
  • L’encapsulation du CSS via les composants mono-fichier (Single-File Component)
  • Du Server Side-Rendering
  • Un outillage en ligne de commande Vue-CLI qui permet de bootstrapper très rapidement un site web
  • Un scale-down très efficace ce qui est pratique pour moderniser uniquement une partie d’un site existant

Build via WebPack

C’est en quelque sorte le successeur de Gulp et Grunt. Il s’en inspire tout en les améliorant, notamment au niveau de la simplicité d’utilisation. Malgré ses inspirations, c’est plutôt un bundler qu’un task runner. Chaque partie de l’application sera interprétée comme un module (Javascript, CSS, image…) et WebPack va construire un arbre de dépendance lui permettant ensuite de générer correctement le rendu final sous forme de ressources statiques. Voilà un aperçu de ce que permet Webpack :

  • Hot Module Replacement (HMR) extrêmement pratique en phase de développement. Contrairement au live-reload qui rafraîchit la page lorsque le code change, le HMR met à jour le code existant sans rafraichissement !
  • Analyse de code (ESLint, CSS Lint…)
  • Transpile, compile et bundle tout ce que l’on veut !
    • Conversion ES6 -> ES5
    • Compilation SCSS -> CSS
  • Minification
  • Permet autant de configuration possible que d’environnement existant :
    • npm run dev : phase de développement
    • npm run build : compilation pour la production
    • npm run unit : exécution des tests unitaires
    • npm run e2e : exécution des tests end-to-end

Dans mon application, WebPack me permet, via un plugin, de générer automatiquement le script de mon service worker. Le gros avantage de passer par ce type de service (aka. une librairie externe) est que l’on va obtenir rapidement un code de qualité avec un haut niveau de sécurité (les bugs sont logiquement corrigés rapidement), comparativement à l’effort supplémentaire important demandé en cas d’écriture complète de notre propre script. Ci-dessous, l’extrait du contenu du fichier webpack.prod.conf permettant de faire cela :

// service worker caching
new SWPrecacheWebpackPlugin({
  cacheId: 'pwa-sample',
  filename: 'service-worker.js',
  staticFileGlobs: \['dist/\*\*/\*.{js,html,css}'\],
  minify: true,
  stripPrefix: 'dist/'
})

Il reste à customiser ce script pour qu’il puisse mettre en cache les données propres à notre application. Dans mon cas, j’ai souhaité prendre en compte les fonts Google et les appels aux différents endpoints de l’API Brewdog’s :

runtimeCaching: \[
        {
          urlPattern: /^https:\\/\\/fonts\\.googleapis\\.com\\//,
          handler: 'cacheFirst'
        },        
        {
          urlPattern: /^https:\\/\\/api\\.punkapi\\.com\\/v2\\/beers/,
          handler: 'cacheFirst'
        },
        {
          urlPattern: /^https:\\/\\/api\\.punkapi\\.com\\/v2\\/beers\\/?id/,
          handler: 'cacheFirst'
        },
        {
          urlPattern: /^https:\\/\\/images\\.punkapi\\.com\\/v2\\//,
          handler: 'cacheFirst'
        }\]
    })

Accès aux périphériques

Les PWA permettent évidemment l’accès aux périphériques externes. Pour illustration, je vais prendre l’exemple de l’appareil photo via l’API MediaDevices. Le fonctionnement est ultra-simple :

  • J’ajoute une vue CameraView.vue dans mon projet
  • Je positionne un tag <video> dans le template de ma vue
  • J’initialise l’appareil photo et l’affecte en tant que source du tag lors de l’évènement mounted
  • Et c’est tout !

A l’affichage, le navigateur se chargera de demander à l’utilisateur l’autorisation d’utiliser son périphérique vidéo et affichera ensuite l’image en provenance de la caméra.

<template>
  <div class="camera-modal">
      <video ref="video" />
  </div>
</template>

<script>
  export default {
    mounted () {
      navigator.mediaDevices.getUserMedia({ video: true })
        .then(mediaStream => {
          this.$refs.video.srcObject = mediaStream
          this.$refs.video.play()
        })
        .catch(error => console.error('getUserMedia() error:', error))
    }
  }
</script>

<style scoped>
  
</style>

Il est ensuite possible d’enrichir l’application avec la prise et l’enregistrement de photo et de vidéo, pour ensuite les envoyer vers un serveur distant (un backoffice dans le cloud par exemple).

Conclusion

Pour conclure, les Progressives Web Apps sont un ensemble de technologies extrêmement prometteuses. C’est pour moi une excellente alternative aux solutions traditionnelles de développement mobile, avec de nombreux avantages, comme l’indépendance vis à vis des stores d’applications, la rapidité de développement et la simplicité d’usage pour les utilisateurs finaux. Elles ne remplaceront jamais (pas encore ?!) les applications natives pour les besoins spécifiques (jeux vidéo, réalité virtuelle/augmentée, application complexe…) mais elles sont parfaitement adaptées à la publication de contenu (blog, site de presse, site vitrine ou institutionnel…), au e-commerce et aux applications métiers simples (mise à disposition de contenu, saisie d’information sur terrain…).

Il ne me reste plus qu’à pousser cette technologie lors d’un prochain projet d’entreprise ;)