Réduire et compresser les charges utiles du réseau avec gzip

Cet atelier de programmation explique comment la réduction et la compression du bundle JavaScript pour l'application suivante améliore les performances de la page en réduisant la taille de la requête de l'application.

Capture d'écran de l'application

Mesurer

Avant de vous lancer dans l'ajout d'optimisations, il est toujours judicieux d'analyser d'abord l'état actuel de l'application.

  • Pour prévisualiser le site, appuyez sur View App (Afficher l'application), puis sur Fullscreen (Plein écran) plein écran.

Cette application, également abordée dans l'atelier de programmation Supprimer le code inutilisé, vous permet de voter pour votre chaton préféré. 🐈

Voyons maintenant la taille de cette application:

  1. Appuyez sur Ctrl+Maj+J (ou Cmd+Option+J sur Mac) pour ouvrir les outils de développement.
  2. Cliquez sur l'onglet Réseau.
  3. Cochez la case Disable cache (Désactiver le cache).
  4. Actualisez l'application.

Taille d'origine du groupe dans le panneau "Réseau"

Bien que de nombreux progrès aient été réalisés dans l'atelier de programmation Supprimer le code inutilisé pour réduire la taille de ce bundle, 225 Ko reste assez volumineux.

Réduction

Prenons l'exemple du bloc de code suivant.

function soNice() {
  let counter = 0;

  while (counter < 100) {
    console.log('nice');
    counter++;
  }
}

Si cette fonction est enregistrée dans un fichier qui lui est propre, la taille du fichier est d'environ 112 B (bytes).

Si tous les espaces sont supprimés, le code obtenu se présente comme suit:

function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}

La taille du fichier est maintenant d'environ 83 octets. S'il s'allonge encore davantage en réduisant la longueur du nom de la variable et en modifiant certaines expressions, le code final peut ressembler à ceci:

function soNice(){for(let i=0;i<100;)console.log("nice"),i++}

La taille du fichier atteint désormais 62 octets.

À chaque étape, le code devient plus difficile à lire. Cependant, le moteur JavaScript du navigateur interprète chacun de ces éléments exactement de la même manière. L'avantage d'obscurcir le code de cette manière peut aider à réduire la taille des fichiers. 112 octets n'était vraiment pas grand-chose au départ, mais il y a quand même eu une réduction de 50 % de la taille !

Dans cette application, la version 4 de webpack est utilisée en tant que bundler de modules. La version spécifique est visible dans package.json.

"devDependencies": {
  //...
  "webpack": "^4.16.4",
  //...
}

La version 4 réduit déjà la taille du bundle par défaut en mode production. Il utilise TerserWebpackPlugin un plug-in pour Terser. Terser est un outil couramment utilisé pour compresser du code JavaScript.

Pour vous faire une idée de la taille du code réduit, cliquez sur main.bundle.js dans le panneau Network (Réseau) des outils de développement. Cliquez maintenant sur l'onglet Response (Réponse).

Réponse réduite

Le code dans sa forme finale, réduit et tronqué, est affiché dans le corps de la réponse. Pour connaître la taille du bundle sans réduction, ouvrez webpack.config.js et mettez à jour la configuration mode.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

Rechargez l'application et examinez à nouveau la taille du bundle via le panneau Network (Réseau) des outils de développement.

Taille du bundle de 767 Ko

C'est une grande différence ! 😅

Veillez à annuler les modifications avant de continuer.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

L'inclusion d'un processus permettant de réduire la taille du code dans votre application dépend des outils que vous utilisez:

  • Si vous utilisez webpack v4 ou une version ultérieure, aucune action supplémentaire n'est requise, car la taille du code est réduite par défaut en mode production. 👍
  • Si une version plus ancienne de webpack est utilisée, installez et incluez TerserWebpackPlugin dans le processus de compilation webpack. La documentation explique ce point en détail.
  • D'autres plug-ins de minimisation existent également et peuvent être utilisés à la place, tels que BabelMinifyWebpackPlugin et ClosureCompilerPlugin.
  • Si un bundler de module n'est pas utilisé du tout, utilisez Terser en tant qu'outil d'interface de ligne de commande ou incluez-le directement en tant que dépendance.

Compression

Bien que le terme "compression" soit parfois utilisé de manière vague pour expliquer comment le code est réduit lors du processus de minimisation, il n'est pas réellement compressé au sens littéral.

La compression fait généralement référence au code qui a été modifié à l'aide d'un algorithme de compression de données. Contrairement à la minimisation, qui finit par fournir un code parfaitement valide, le code compressé doit être décompressé avant d'être utilisé.

Pour chaque requête et réponse HTTP, les navigateurs et les serveurs Web peuvent ajouter des headers pour inclure des informations supplémentaires sur l'élément récupéré ou reçu. Vous pouvez le voir dans l'onglet Headers du panneau DevTools Network, où trois types sont affichés:

  • General représente les en-têtes généraux pertinents pour l'ensemble de l'interaction requête-réponse.
  • Le tableau de bord En-têtes de réponse contient une liste d'en-têtes spécifiques à la réponse réelle du serveur.
  • Le menu En-têtes de requête contient la liste des en-têtes joints à la requête par le client.

Examinez l'en-tête accept-encoding dans Request Headers.

Accepter l&#39;en-tête d&#39;encodage

accept-encoding permet au navigateur de spécifier les formats d'encodage de contenu ou les algorithmes de compression qu'il accepte. Il existe de nombreux algorithmes de compression de texte, mais seuls trois sont compatibles avec la compression (et la décompression) des requêtes réseau HTTP:

  • Gzip (gzip): format de compression le plus utilisé pour les interactions avec le serveur et le client. Il s'appuie sur l'algorithme Deflate et est compatible avec tous les navigateurs actuels.
  • Deflate (deflate): peu utilisé.
  • Brotli (br): algorithme de compression plus récent qui vise à améliorer davantage les taux de compression, ce qui peut accélérer le chargement des pages. Il est compatible avec les dernières versions de la plupart des navigateurs.

L'exemple d'application de ce tutoriel est identique à celui décrit dans l'atelier de programmation Supprimer le code inutilisé, à la différence près que Express est désormais utilisé comme framework de serveur. Dans les sections suivantes, nous allons aborder la compression statique et dynamique.

Compression dynamique

La compression dynamique consiste à compresser les éléments à la volée, à mesure qu'ils sont demandés par le navigateur.

Avantages

  • Il n'est pas nécessaire de créer et de mettre à jour des versions compressées enregistrées des éléments.
  • La compression à la volée fonctionne particulièrement bien pour les pages Web générées dynamiquement.

Inconvénients

  • La compression des fichiers à des niveaux supérieurs pour obtenir de meilleurs taux de compression prend plus de temps. Cela peut entraîner une perte de performances, car l'utilisateur attend que les éléments soient compressés avant qu'ils ne soient envoyés par le serveur.

Compression dynamique avec Node/Express

Le fichier server.js est responsable de la configuration du serveur de nœuds qui héberge l'application.

const express = require('express');

const app = express();

app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});

Actuellement, il s'agit d'importer express et d'utiliser le middleware express.static pour charger tous les fichiers HTML, JS et CSS statiques dans le répertoire public/ (ces fichiers sont créés par webpack à chaque compilation).

Pour vous assurer que tous les éléments sont compressés à chaque requête, vous pouvez utiliser la bibliothèque middleware compression. Commencez par l'ajouter en tant que devDependency dans package.json:

"devDependencies": {
  //...
  "compression": "^1.7.3"
},

Importez-le ensuite dans le fichier serveur server.js:

const express = require('express');
const compression = require('compression');

Ajoutez-le en tant que middleware avant l'installation de express.static:

//...

const app = express();

app.use(compression());

app.use(express.static('public'));

//...

Maintenant, actualisez l'application et examinez la taille du bundle dans le panneau Network (Réseau).

Taille du bundle avec compression dynamique

De 225 Ko à 61,6 Ko Dans Response Headers maintenant, un en-tête content-encoding indique que le serveur envoie ce fichier encodé avec gzip.

En-tête d&#39;encodage du contenu

Compression statique

L'idée derrière la compression statique est de compresser les éléments et de gagner du temps.

Avantages

  • La latence due à des niveaux de compression élevés n'est plus une préoccupation. Rien ne doit se produire à la volée pour compresser les fichiers, car ils peuvent désormais être récupérés directement.

Inconvénients

  • Les éléments doivent être compressés à chaque compilation. Les durées de compilation peuvent augmenter considérablement si des niveaux de compression élevés sont utilisés.

Compression statique avec Node/Express et webpack

Étant donné que la compression statique implique la compression de fichiers à l'avance, les paramètres webpack peuvent être modifiés pour compresser les éléments dans le cadre de l'étape de compilation. Pour ce faire, vous pouvez utiliser CompressionPlugin.

Commencez par l'ajouter en tant que devDependency dans package.json:

"devDependencies": {
  //...
  "compression-webpack-plugin": "^1.1.11"
},

Comme pour tout autre plug-in webpack, importez-le dans le fichier de configuration webpack.config.js:.

const path = require("path");

//...

const CompressionPlugin = require("compression-webpack-plugin");

Et incluez-le dans le tableau plugins:

module.exports = {
  //...
  plugins: [
    //...
    new CompressionPlugin()
  ]
}

Par défaut, le plug-in compresse les fichiers de compilation à l'aide de gzip. Consultez la documentation pour savoir comment ajouter des options permettant d'utiliser un autre algorithme ou inclure/exclure certains fichiers.

Lorsque l'application est actualisée et recréée, une version compressée du bundle principal est créée. Ouvrez la console Glitch pour examiner le contenu du répertoire public/ final diffusé par le serveur Node.

  • Cliquez sur le bouton Tools (Outils).
  • Cliquez sur le bouton Console.
  • Dans la console, exécutez les commandes suivantes pour accéder au répertoire public et afficher tous ses fichiers:
cd public
ls

Fichiers finaux générés dans le répertoire public

La version du bundle compressée avec gzip, main.bundle.js.gz, est maintenant également enregistrée ici. CompressionPlugin compresse également index.html par défaut.

La prochaine étape consiste à indiquer au serveur d'envoyer ces fichiers compressés avec gzip chaque fois que leurs versions JS d'origine sont demandées. Pour ce faire, définissez une nouvelle route dans server.js avant que les fichiers ne soient diffusés avec express.static.

const express = require('express');
const app = express();

app.get('*.js', (req, res, next) => {
  req.url = req.url + '.gz';
  res.set('Content-Encoding', 'gzip');
  next();
});

app.use(express.static('public'));

//...

app.get permet d'indiquer au serveur comment répondre à une requête GET pour un point de terminaison spécifique. Une fonction de rappel est ensuite utilisée pour définir comment gérer cette requête. L'itinéraire fonctionne de la manière suivante:

  • Si vous spécifiez '*.js' comme premier argument, cela signifie que cela fonctionne pour chaque point de terminaison déclenché pour récupérer un fichier JS.
  • Dans le rappel, .gz est associé à l'URL de la requête et l'en-tête de réponse Content-Encoding est défini sur gzip.
  • Enfin, next() garantit que la séquence continue à tous les rappels éventuellement suivants.

Une fois l'application actualisée, consultez à nouveau le panneau Network.

Réduction de la taille de bundle avec la compression statique

Comme précédemment, la taille du lot a été nettement réduite !

Conclusion

Cet atelier de programmation a abordé le processus de minimisation et de compression du code source. Ces deux techniques deviennent l'option par défaut dans de nombreux outils disponibles aujourd'hui. Il est donc important de déterminer si votre chaîne d'outils les prend déjà en charge ou si vous devez commencer à appliquer les deux processus vous-même.