Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Progressive Web App
  3. Performances générales
Extrait - Progressive Web App Utilisez les standards du web pour développer vos applications mobiles
Extraits du livre
Progressive Web App Utilisez les standards du web pour développer vos applications mobiles Revenir à la page d'achat du livre

Performances générales

Introduction

Avant d’aborder la nouvelle version du protocole HTTP au chapitre HTTP/2, il est primordial de présenter les bonnes pratiques que nous devons mettre en place dans nos applications.

Lorsqu’un navigateur affiche un site web, un processus bien normé est respecté afin de télécharger les ressources statiques nécessaires. En effet, si nous allons sur le site.eni.fr par exemple, le navigateur télécharge tout d’abord le fichier index.html disponible à cette adresse. Une fois le fichier téléchargé, il commence à le lire, autrement dit à le parser (de l’anglais to parse), et dès qu’il rencontre une balise nécessitant une nouvelle requête réseau (une balise script pour un fichier JavaScript ou une balise link pour une feuille de style), il met en pause la phase de parsing de la page afin de télécharger et d’exécuter la ressource demandée.

Ce processus n’oblige pas le téléchargement d’une seule ressource à la fois. En effet, les navigateurs permettent de télécharger environ six requêtes simultanées par domaine (ce chiffre peut différer en fonction du navigateur). Ce que nous appelons le HoL Blocking (Head of Line Blocking).

Au fur et à mesure de l’histoire du Web, alors que les applications sont devenues...

Concaténation

L’une des solutions est de concaténer l’ensemble des ressources statiques de même type afin de limiter le nombre de requêtes nécessaires. Cette tâche est bien évidemment automatisée via l’utilisation d’outils dédiés : Webpack, Gulp, Grunt,…

Dans l’exemple ci-après, nous avons une configuration Gulp permettant de concaténer l’ensemble des fichiers se trouvant le répertoire lib dans un fichier cible dist/all.js.

const concat = require('gulp-concat'); 
  gulp.task('scripts', function() { 
    return gulp.src('./lib/*.js') 
      .pipe(concat('all.js')) 
      .pipe(gulp.dest('./dist/')); 
  }); 

Il est tout de même important d’indiquer que la solution magique n’est pas de concaténer l’ensemble des fichiers dans un seul. En effet, ce choix génère des fichiers très volumineux qui nécessitent un temps de chargement conséquent par le navigateur.

Il est préférable de générer plusieurs fichiers moins importants. Nous pouvons imaginer un fichier avec le code applicatif et un deuxième avec les dépendances (souvent nommé vendor.js). Ce découpage...

Suppression du code non nécessaire

Est-il nécessaire de demander au navigateur de télécharger du code qui n’est pas utilisé ? Ou qui n’est pas utilisé souvent par l’utilisateur ? La réponse est bien évidemment non. Que pouvons-nous mettre en place pour faire cette optimisation ?

Nous pouvons tout d’abord minifier le code source. Cela consiste à supprimer tous les caractères non nécessaires au bon fonctionnement de l’application (tabulations, retours chariot…). Nous pouvons par exemple utiliser le module NPM Uglify pour faire ce traitement. Dans les développements actuels utilisant souvent Webpack, la minification est automatiquement réalisée lorsque nous générons le livrable final (en mode production).

Une fois le code minifié, nous pouvons l’optimiser. Cette tâche consiste à réécrire de manière optimisée le code applicatif afin qu’il prenne le moins de place possible. Pour cela, nous pouvons par exemple utiliser le module NPM terser ou le compilateur Closure dans le workflow de génération du livrable final. 

Ces deux étapes implémentées, nous pouvons faire mieux, à savoir supprimer le code qui n’est pas utilisé. Dans le code présenté ci-après, nous avons un fichier JavaScript...

Gestion des images

Les images sont des éléments très importants pour une application, permettant soit de faire passer un message soit d’égayer le design général. Nous allons présenter les bonnes pratiques que nous pouvons mettre en place pour optimiser leur utilisation :

  • recours à des sprites

  • utilisation de la balise picture

  • ajout de l’attribut loading

  • mise en place du domaine sharding

  • manipulation de l’API IntersectionObserver

L’objectif principal est de limiter au maximum la taille des images que nous téléchargeons.

1. Sprites

L’une des premières pratiques à mettre en place repose sur l’usage des sprites. En effet, cette solution consiste à agréger toutes les images de l’application (souvent les toutes petites images) en une image unique afin de réduire le nombre de téléchargements par le navigateur.

Se pose alors la question de la sélection de l’image que nous souhaitons utiliser au sein de l’image unique. Pour cela, nous utilisons le positionnement CSS via la propriété background-position.

Dans l’exemple ci-après, nous appliquons une sprite comme background d’un élément CSS. Via la propriété CSS background-position, nous décalons l’origine de l’image, afin de sélectionner la partie qui nous intéresse (en combinaison de l’utilisation des propriétés width et height).

.flag { 
  background-image: url(‘./images/flags.png'); 
  background-repeat: no-repeat;  
} 
.flag.fr { 
  background-position: -5px -20px; 
  width: 25px; 
  height: 15px; 
} 

2. Balise picture

L’utilisation des sprites permet de résoudre un cas d’utilisation, mais pas tous. Par exemple, cette solution n’est pas à envisager pour les images de haute qualité, car cela pourrait générer une sprite de trop grosse taille.

Si nous souhaitons optimiser les images de haute qualité pour, par exemple, ne charger que la version avec la résolution optimale pour l’utilisateur, afin de toujours limiter les octets téléchargés, nous allons utiliser l’élément HTML...

Chargement des fichiers JavaScript

Lors du chargement de certaines ressources statiques, nous pouvons utiliser les attributs async et defer afin de modifier le comportement du navigateur. Pour rappel, par défaut, lorsque le navigateur détecte une balise script, il télécharge le script associé et l’exécute de manière bloquante. Nous pouvons rendre ce processus non bloquant pour certains fichiers JavaScript non nécessaires pour le chargement initial de la page principale.

Grâce à ces attributs, le navigateur lancera le téléchargement en background sans bloquer le rendu de l’application principale. La différence entre ces deux attributs se situe dans l’exécution. En effet, avec l’attribut async, le fichier est évalué le plus tôt possible en bloquant la génération de la page. L’attribut defer quant à lui s’exécute à la fin du chargement de la page.

Dans l’exemple ci-après, nous utilisons les attributs async et defer pour charger deux fichiers JavaScript distincts.

<script async src="analytics.js"> 
<script defer src="tag-manager.js">  

Une autre bonne pratique est d’établir le plus tôt possible la connexion pour télécharger une ressource statique primordiale hébergée sur un autre nom de domaine....

Priority hints

Cette API en cours de standardisation, ne fonctionnant pour l’instant que sur Chrome à travers un flag, permet de définir des priorités sur des requêtes HTTP envoyées par une application. Ce nouveau standard définit un nouvel attribut, importance, pouvant prendre trois valeurs : low, high ou auto.

La valeur auto indique que le développeur ne souhaite pas définir de priorité pour la requête. Cette valeur est également utilisée si celle passée par le développeur est invalide.

Cet attribut peut être utilisé à plusieurs endroits : sur des balises img, link ou encore script, mais également lors de l’utilisation de l’API Fetch afin de faire une requête vers une API.

Voici plusieurs exemples de cet attribut sur différents éléments HTML.

<img src="main-picture.jpg" importance="high"> 
<script src="social.js" importance="low"></script> 
<link rel="stylesheet" href="social.css" importance="low"> 
 
<script> 
  fetch('/api/metadata', { 
    importance: 'high' 
  }).then(/*...*/) 
</script> 

Server-side rendering

Pour finir, nous allons mentionner une nouvelle pratique de plus en plus courante : le server-side rendering ou la génération des pages côté serveur. Certes, ce mécanisme existe depuis très longtemps, mais depuis plusieurs années, une migration de la gestion des pages s’est opérée du serveur vers le navigateur, grâce à des frameworks permettant d’implémenter des Single Page Applications. Nous pouvons bien évidemment citer Angular, React ou encore Vue.js.

Ce changement de paradigme n’est pas en train de s’inverser. Mais nous voyons plutôt une utilisation conjointe de la génération des pages à la fois côté serveur et côté navigateur. La page est tout d’abord générée côté serveur, et une fois reçue par le navigateur, la librairie ou le framework utilisé reprend la main.

En plus d’améliorer le résultat en termes de référencement, le fait d’ajouter du rendu côté serveur à une application web permet d’optimiser le temps de chargement de la page, et ainsi de réduire le temps nécessaire pour que l’utilisateur puisse commencer à l’utiliser. Cette pratique permet d’avoir des résultats intéressants, car :

  • Le serveur...

Application fil rouge

Nous n’allons pas mettre en pratique tous les points vus précédemment dans l’application fil rouge. Nous allons seulement nous limiter à l’ajout de l’outil Webpack, permettant de générer une version optimisée de l’application.

Nous allons tout d’abord initialiser un projet Node dans notre répertoire de travail. Cela nous permettra de créer un fichier package.json dans lequel nous pourrons ajouter des dépendances nécessaires pour notre workflow de build.

npm init --yes 

Une fois le fichier package.json créé, nous installons les premières dépendances, webpack et webpack-cli, via la méthode install de l’outil NPM.

npm install -D webpack webpack-cli 

Le fichier package.json ressemble à présent à ceci :

{ 
  "name": "eni-pwa", 
  "version": "1.0.0", 
  "description": "", 
  "main": "script.js", 
  "dependencies": { 
      }, 
  "devDependencies": { 
    "webpack": "^4.43.0", 
    "webpack-cli": "^3.3.11" 
  }, 
  "scripts": { 
    "test": "echo \"Error: no test specified\" && exit 1" 
  }, 
  "repository": { 
    "type": "git", 
    "url": "git+https://github.com/EmmanuelDemey/eni-pwa.git" 
  }, 
  "keywords": [], 
  "author": "", 
  "license": "ISC", 
  "bugs": { 
    "url": "https://github.com/EmmanuelDemey/eni-pwa/issues" 
  }, 
  "homepage": "https://github.com/EmmanuelDemey/eni-pwa#readme" 
} 

Nous sommes prêts pour configurer Webpack, afin d’optimiser l’application. Pour cela, nous créons un fichier webpack.config.js...