Supprimer le code inutilisé

Dans cet atelier de programmation, vous allez améliorer les performances de l'application suivante en supprimant toutes les dépendances inutilisées et inutiles.

Capture d'écran de l'application

Mesurer

Il est toujours recommandé de commencer par mesurer les performances d'un site Web avant d'ajouter des optimisations.

  • Pour prévisualiser le site, appuyez sur Afficher l'application, puis sur Plein écran plein écran.

N'hésitez pas à cliquer sur votre chaton préféré. Realtime Database de Firebase est utilisé dans cette application. C'est pourquoi le score est mis à jour en temps réel et synchronisé avec toutes les autres personnes qui utilisent l'application. 🐈

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

Taille du groupe d'origine : 992 ko

Près d'1 Mo de code JavaScript est envoyé pour charger cette application simple.

Examinez les avertissements du projet dans DevTools.

  • Cliquez sur l'onglet Console.
  • Assurez-vous que Warnings est activé dans le menu déroulant des niveaux à côté de l'entrée Filter.

Filtre "Avertissements"

  • Examinez l'avertissement affiché.

Avertissement de la console

Firebase, qui est l'une des bibliothèques utilisées dans cette application, joue le rôle du bon samaritain en fournissant un avertissement pour indiquer aux développeurs de ne pas importer l'intégralité de son package, mais uniquement les composants utilisés. En d'autres termes, il existe des bibliothèques inutilisées qui peuvent être supprimées dans cette application pour accélérer son chargement.

Il existe également des cas où une bibliothèque particulière est utilisée, mais où il peut exister une alternative plus simple. Le concept de suppression des bibliothèques inutiles est abordé plus loin dans ce tutoriel.

Analyser le groupe

L'application comporte deux dépendances principales:

  • Firebase: plate-forme qui fournit un certain nombre de services utiles pour les applications iOS, Android ou Web. Ici, sa base de données en temps réel est utilisée pour stocker et synchroniser les informations de chaque chaton en temps réel.
  • Moment.js: bibliothèque d'utilitaires qui facilite la gestion des dates en JavaScript. La date de naissance de chaque chaton est stockée dans la base de données Firebase, et moment est utilisé pour calculer son âge en semaines.

Comment deux dépendances seulement peuvent-elles contribuer à une taille de bundle de près d'1 Mo ? L'une des raisons est que toute dépendance peut à son tour avoir ses propres dépendances. Il y en a donc beaucoup plus que deux si chaque profondeur/branche de l'"arbre" de dépendance est prise en compte. Une application peut facilement devenir volumineuse relativement rapidement si de nombreuses dépendances sont incluses.

Analysez le bundler pour mieux comprendre ce qui se passe. Plusieurs outils créés par la communauté peuvent vous aider à le faire, comme webpack-bundle-analyzer.

Le package de cet outil est déjà inclus dans l'application en tant que devDependency.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

Cela signifie qu'il peut être utilisé directement dans le fichier de configuration webpack. Importez-le au tout début de webpack.config.js:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

Ajoutez-le maintenant en tant que plug-in à la toute fin du fichier dans le tableau plugins:

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

Lorsque l'application se recharge, une visualisation de l'ensemble du bundle doit s'afficher au lieu de l'application elle-même.

Webpack Bundle Analyzer

Ce n'est pas aussi mignon que de voir des chatons 🐱, mais c'est tout de même très utile. Pointez sur l'un des packages pour voir sa taille représentée de trois manières différentes:

Taille de la statistique Taille avant toute minification ou compression.
Taille analysée Taille du package réel dans le bundle après sa compilation. La version 4 de webpack (qui est utilisée dans cette application) minimise automatiquement les fichiers compilés. C'est pourquoi ils sont plus petits que la taille des statistiques.
Taille compressée Taille du package après compression avec l'encodage gzip. Ce sujet est abordé dans un guide distinct.

L'outil webpack-bundle-analyzer permet d'identifier plus facilement les packages inutilisés ou inutiles qui représentent une grande partie du bundle.

Supprimer les packages inutilisés

La visualisation montre que le package firebase comprend beaucoup plus qu'une simple base de données. Il inclut des packages supplémentaires, tels que:

  • firestore
  • auth
  • storage
  • messaging
  • functions

Il s'agit de services incroyables fournis par Firebase (consultez la documentation pour en savoir plus). Toutefois, aucun d'eux n'est utilisé dans l'application. Il n'y a donc aucune raison de les importer tous.

Annulez les modifications dans webpack.config.js pour afficher à nouveau l'application:

  • Supprimez BundleAnalyzerPlugin de la liste des plug-ins:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • Supprimez maintenant l'importation inutilisée en haut du fichier:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

L'application devrait maintenant se charger normalement. Modifiez src/index.js pour mettre à jour les importations Firebase.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

Lorsque l'application se recharge, l'avertissement DevTools ne s'affiche plus. L'ouverture du panneau Network (Réseau) de DevTools montre également une belle réduction de la taille du lot:

Taille du bundle réduite à 480 Ko

Plus de la moitié de la taille du bundle a été supprimée. Firebase fournit de nombreux services différents et permet aux développeurs de n'inclure que ceux dont ils ont réellement besoin. Dans cette application, seul firebase/database a été utilisé pour stocker et synchroniser toutes les données. L'importation firebase/app, qui configure la surface de l'API pour chacun des différents services, est toujours requise.

De nombreuses autres bibliothèques populaires, telles que lodash, permettent également aux développeurs d'importer sélectivement différentes parties de leurs packages. Sans trop d'efforts, la mise à jour des importations de bibliothèques dans une application pour n'inclure que ce qui est utilisé peut entraîner des améliorations significatives des performances.

Bien que la taille du bundle ait été considérablement réduite, il reste encore du travail à faire ! 😈

Supprimer les packages inutiles

Contrairement à Firebase, l'importation de parties de la bibliothèque moment ne peut pas être effectuée aussi facilement, mais peut-être peut-elle être supprimée complètement ?

L'anniversaire de chaque chaton mignon est stocké au format Unix (millisecondes) dans la base de données Firebase.

Dates de naissance stockées au format Unix

Il s'agit d'un code temporel d'une date et d'une heure particulières, représenté par le nombre de millisecondes écoulées depuis le 1er janvier 1970 à 00:00 UTC. Si la date et l'heure actuelles peuvent être calculées au même format, une petite fonction permettant de trouver l'âge de chaque chaton en semaines peut probablement être créée.

Comme toujours, essayez de ne pas copier-coller les commandes au fur et à mesure. Commencez par supprimer moment des importations dans src/index.js.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

Un écouteur d'événements Firebase gère les modifications de valeur dans notre base de données:

favoritesRef.on("value", (snapshot) => { ... })

Au-dessus de cela, ajoutez une petite fonction pour calculer le nombre de semaines à partir d'une date donnée:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

Dans cette fonction, la différence en millisecondes entre la date et l'heure actuelles (new Date).getTime() et la date de naissance (l'argument birthDate, déjà en millisecondes) est calculée et divisée par le nombre de millisecondes dans une semaine.

Enfin, toutes les instances de moment peuvent être supprimées dans l'écouteur d'événements en utilisant plutôt cette fonction:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

Rechargez l'application et examinez à nouveau le panneau Network (Réseau).

Taille du bundle réduite à 225 Ko

La taille de notre bundle a été réduite de plus de la moitié.

Conclusion

Grâce à cet atelier de programmation, vous devriez avoir une bonne compréhension de la façon d'analyser un bundle particulier et pourquoi il peut être si utile de supprimer les packages inutilisés ou inutiles. Avant de commencer à optimiser une application avec cette technique, il est important de savoir que cela peut être beaucoup plus complexe dans les applications plus volumineuses.

Pour supprimer les bibliothèques inutilisées, essayez de déterminer quelles parties d'un bundle sont utilisées et lesquelles ne le sont pas. Si un paquet semble mystérieux et qu'il ne semble pas être utilisé nulle part, prenez du recul et vérifiez quelles dépendances de niveau supérieur pourraient en avoir besoin. Essayez de trouver un moyen de les dissocier.

La suppression des bibliothèques inutiles peut s'avérer un peu plus compliquée. Il est important de travailler en étroite collaboration avec votre équipe pour voir s'il est possible de simplifier certaines parties du codebase. Il peut sembler que supprimer moment dans cette application soit la bonne chose à faire à chaque fois, mais que se passerait-il s'il y avait des fuseaux horaires et des paramètres régionaux différents à gérer ? Ou s'il y avait des manipulations de date plus complexes ? La manipulation et l'analyse des dates et heures peuvent s'avérer très délicates. Les bibliothèques telles que moment et date-fns simplifient considérablement ce processus.

Tout est un compromis, et il est important de déterminer s'il est même utile de déployer une solution personnalisée au lieu de s'appuyer sur une bibliothèque tierce.