Progress web app (PWA)

Table des matières

1 Présentation	2

2 Exemple	2

3 Architecture	2

4 Web worker	2

4.1 Service worker	3

4.2 Cycle de vie	3

5 Mises à jour	3

6 Installation	3

6.1 Manifeste	3

7 Notifications	4

7.1 Demande de permission	4

7.2 Push	4

8 Optimisation du chargement	4

8.1 Chargement différé	4

8.2 Images	4

9 Développement	5

9.1 Angular	5

9.1.1 Exemple minimal	5

10 Installation de l’application	6

10.1 Création de l’icône de lancement sur le bureau	6

10.1.1 Chrome	6

10.1.2 Firefox	7

Présentation

cf. https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps

cf. https://web.dev/learn/pwa/architecture/

Ce n’est pas un framework intégré mais plutôt un ensemble de technos pour concevoir des applis web avec les caractéristiques suivantes :

découvrable : un moteur de recherche peut détecter l’appli

installable : ajout d’une icône sur le bureau pour lancer l’appli

liable : on peut partager une URL qui permet de lancer l’appli

indépendante du réseau : fonctionne avec une connexion réseau faible/inexistante

les PWA font un usage intensive des service workers pour contrôler les requêtes envoyées par les pages et les ressources reçues (mise en cache par ex)

stockage local par Web Storage (API synchrone !) et IndexedDB

progressive : qui s’adapte aux navigateurs récents et anciens

notifiable : des messages et des mises à jour peuvent être envoyées vers l’appli depuis le web

responsive : s’adapte à la taille de fenêtre/écran

sécurisée

Exemple

Source : https://github.com/mdn/pwa-examples/tree/master/js13kpwa

Exé : https://mdn.github.io/pwa-examples/js13kpwa/

Architecture

Un squelette d’application permet un démarrage rapide avec un contenu enrichi par la suite (par le contenu du cache ou par des requêtes). Une alternative, plus complexe mais plus réactive, est l’API Streams.

Structure minimale :

index.html

code Javascript de l’appli

code Javascript d’un service worker

des ressources

un manifeste

Le service worker peut à son installation (cf. 4.2) mettre en cache le code de l’appli et des ressources (locales ou web).

Web worker

C’est un moyen d’exécuter un script dans un thread séparé, ce qui permet de ne pas bloquer/ralentir l’interface utilisateur.

Un calcul peut être confié à un web worker dont le résultat est récupéré par promise.

Il n’a pas accès au DOM mais peut communiquer avec plusieurs contextes de navigation (fenêtres, onglets). Uniquement en HTTPS.

L’envoi de réception de données se fait par postMessage()/onmessage(), les données échangées ne sont pas partagées mais copiées.

Plusieurs types :

dédié : worker utilisé par un seul script

partagé : utilisé par plusieurs scripts (pages/onglets)

de service : agit comme proxy

L’exécution se fait dans un contexte différent du contexte de navigation.

Service worker

C’est un cas particulier de web worker. Il agit comme un intermédiaire (proxy) entre l’appli et le web. Il intercepte les requêtes de l’appli et leur réponse, peut opérer dessus, lire ou écrire dans un cache.

Stratégie « offline first » : si une ressource est disponible en cache, elle est fournie en réponse à une requête de l’appli, sinon une requête web est lancée.

« Progressive » dans le sens d’apporter un plus si présent, l’absence d’un service worker ne doit pas changer (significativement ?) le comportement de l’appli (seulement ses performances).

Exemples de service workers.

Cycle de vie

Le cycle de vie comprend les étapes suivantes :

Un worker est d'abord enregistré par l’appli.

Installation : se fait lors d’un événement « install » auquel le worker répond par exemple en mettant en cache des ressources/du code.

Activation : autre événement. Par ex pour : suppression de données obsolètes, préparation au démarrage de l’appli, …

Fetch : événement reçu par le worker à chaque requête HTTP de l’appli. Permet par ex d’utiliser le cache plutôt que faire une requête web.

Le worker est conservé quand on ferme l’onglet/le navigateur. Quand on relance l’appli, le worker est relancé avant celle ci.

Mises à jour

Les nouvelles données/code sont mises en cache sous un nouveau « nom », cad une clé qui identifie la version.

L’ancien service worker continue à fonctionner tant que des pages actives en dépendent. Lorsqu’elles se terminent, le nouveau service worker est activé et remplace l’ancien.

L’événement « activate » est utilisé pour nettoyer le cache de l’ancienne version (fichiers associés à l’ancienne clé).

Les mises à jour par Push API peuvent être partielles, la totalité de la nouvelle version n’est pas téléchargée.

Installation

Création d’une icône de lancement de l’appli sur le bureau (ou le « home screen » d’un mobile).

Nécessite :

le manifeste de la web app

l’URL (?) HTTP du site

une icône

un service worker enregistré

Manifeste

Dans la racine de l’appli, au format JSON.

Comporte le titre de l’appli, le chemin des icônes, du splash screen, un flag plein écran, ...

Référencé dans le block <head> de index.html.

Notifications

A travers une API qui permet de présenter à l’utilisateur des alertes et des informations en provenance du serveur.

Fonctionne en tandem avec Push API qui gère la réception de nouveaux fichiers depuis le serveur. Push est une sorte d’extension des notifications puisqu’il s’agit aussi de recevoir de l’information du web.

Demande de permission

Une bonne pratique consiste à demander à l’utilisateur la permission d’afficher des notifications en provenance de l’appli : un popup apparaît pour obtenir sa réponse.

Push

Nécessite une inscription à un serveur qui enverra les nouvelles données au service worker.

Le service worker réagit aux envois de données par le serveur en traitant l’événement push.

Exemple simple de mise en œuvre avec son analyse : un serveur, un service worker, une appli cliente. L’appli commence par enregistrer le service worker et l’inscrire (on récupère au passage la clé publique du serveur). Le serveur utilise web-push et gère une paire publique-privée de clés. Il met en place les routes (URLs ?) pour obtenir la clé publique, s’enregistrer et envoyer des notifications. Le service worker traite les événements push et affiche le texte reçu par une notification à l’utilisateur.

Optimisation du chargement

Chargement différé

L’idée est de télécharger un minimum de données et de code pour afficher quelque chose à l’utilisateur le plus tôt possible. Il va dans le sens inverse de lier par exemple tout le code (ou les feuilles de style) de l’appli dans un seul fichier pour optimiser le chargement. En effet, l’utilisateur ne visualise pas l’entièreté du site web/appli d’un coup.

Plusieurs techniques sont utilisables :

l’attribut defer de la balise script (ex : <script src="app.js" defer></script>) et l’import dynamique de modules JS.

on peut aussi découper les feuilles de style et leur associer un type de media (le navigateur ne les chargera que si nécessaire).

chargement à la demande (lazy loading). Par exemple, ne charge les images que quand elles apparaîssent effectivement à l’écran.

attribut loading="lazy" de l’élément <img>.

Images

On peut réduire la taille des images sans perte de qualité significative (format JPEG, services comme TinyPNG, …).

On peut utiliser des images de substitution :

Ex : une image est spécifiée par

<img src="data/img/placeholder.png" data-src="data/img/SLUG.jpg" alt="NAME"/>

placeholder.png est l’image de substitution. Du code lit ensuite l’attribut data-src pour charger la « vraie » image après l’affichage de la page.

Développement

Doc sur les service workers ici et là.

Test en ligne de la PWA : ici et là.

Angular

Docs Angular :

présentation

App shell

mise en place d’un worker

mise en place d'un Service Worker. Le worker généré de cette façon gère automatiquement la mise en cache des ressources. Pour du code spécifique, ajouter un web worker (ligne ci dessus).

notifications

mises à jour

Le manifeste s’appelle ngsw.json et sa génération est configurée par ngsw-config.json soit lors de ng build, soit manuellement avec :

./node_modules/.bin/ngsw-config ./dist/<project-name> ./ngsw-config.json [/base/href]

La méthode SwUpdate.isEnabled permet de vérifier que le navigateur prend en charge les service workers.

Exemple minimal

Suivre la procédure décrite ici ou lancer le script suivant :

#!/bin/bash

APP_NAME=testPWApp

rm -rf $APP_NAME

# création appli

ng new $APP_NAME --routing --style css

# création app shell

cd $APP_NAME

ng generate app-shell

# ajout service worker

ng add @angular/pwa --project $APP_NAME --skip-confirmation

# installation d'un serveur HTTP pour servir localement l'appli après compilation

npm i http-server

# compilation

ng build

# lancement

SERVER=./node_modules/.bin/http-server

sleep 2 && chromium http://localhost:8080 &

$SERVER -p 8080 -c-1 dist/$APP_NAME/browser

Modifications remarquables : angular.json, ngsw-config.json, src/manifest.webmanifest, src/app/app.module.ts.

Pour vérifier le fonctionnement du service worker :

dans Chrome, au lancement de l’appli, taper F12

supprimer un service worker et un cache éventuels :

onglet « Application »

cliquer à gauche « Application » puis « Storage »

cliquer « Clear site data »

rafraîchir la page et constater que l’application ne fonctionne plus

relancer Chrome à la même adresse (localhost:8080)

constater que l’application fonctionne

pour vérifier que les données sont servies par le service worker :

taper F12

onglet « Network »

rafraîchir la page (F5)

dans la colonne « Size », constater que « (ServiceWorker) » apparaît

onglet « Application » / « Cache Storage » : de nouvelles entrées sont apparues

pour simuler une coupure réseau :

onglet « Network » : à côté de « Disable cache », sélectionner « Offline » (à la place de « No throttling »)

rafraîchir la page (F5)

constater que l’application fonctionne

Installation de l’application

Informations établies le 16/11/2022, sujettes donc à évolution.

Prise en charge de l’installation d’une PWA :

Navigateur\Version	Desktop Linux	Desktop Windows	Android	iOS
Chrome	oui			
Firefox	avec une extension (*)	avec une extension (*)		

(*) pour l’installation depuis Firefox version de bureau, installer l’extension PWAsForFirefox (site GitHub).

L’installation de PWAsForFirefox nécessite celle de paquets/setups supplémentaires, suivre la procédure :

sur la page d’installation de l’extension, cliquer « Add to Firefox »

accepter la licence de PWAsForFirefox

installer la partie « native » de l’extension qui dépend de l’OS (la navigation devrait passer ensuite automatiquement à la page suivante)

installer le « runtime » cad une version spécifique de Firefox qui prend en charge les PWA (la navigation devrait passer ensuite automatiquement à la page suivante)

fin de la procédure d’installation : fermer l’onglet

Création de l’icône de lancement sur le bureau

Chrome

Une fois l’application lancée en utilisant son URL, cliquer sur « ... » en haut à droite de la fenêtre Chrome et choisir « Installer <le nom de la web app>... ».

Après confirmation, l’icône doit apparaître sur le bureau.

Pour désinstaller l’appli, la lancer depuis l’icône, cliquer en haut à droite sur « ... », choisir « Désinstaller <le nom de la web app>... ».

Firefox

Une fois l’application lancée en utilisant son URL, cliquer sur le bouton de l’extension en haut à droite de la fenêtre de Firefox.

Cliquer « Install current site ».

Remplir (ou pas !) les champs et cliquer « Install web app ».

Fermer le menu en cliquant sur et le rouvrir. L’application apparaît dans la liste :

Cliquer sur l’icône à droite du nom de l’application pour la lancer.

Sous Linux, pour ajouter une icône sur le bureau :

Ouvrir le menu de démarrage des applications

Rechercher l’application PWA par son nom

Bouton droit de la souris sur le nom → Ajouter au bureau

L’icône apparaît sur le bureau.

2022-11-14T13:42:51.293715008