Réduire les charges utiles JavaScript avec le fractionnement de code

La plupart des pages Web et des applications sont constituées de nombreuses parties différentes. Au lieu d'envoyer tout le code JavaScript qui constitue l'application dès le chargement de la première page, le fait de le diviser en plusieurs blocs améliore les performances de la page.

Cet atelier de programmation vous explique comment utiliser le fractionnement du code pour améliorer les performances d'une application simple qui trie trois nombres.

Une fenêtre de navigateur affiche une application intitulée "Magic Sorter" (Trieur magique) avec trois champs pour saisir des nombres et un bouton de tri.

Mesurer

Comme toujours, il est important de mesurer d'abord les performances d'un site Web avant d'essayer d'y ajouter des optimisations.

  1. Pour prévisualiser le site, appuyez sur Afficher l'application, puis sur Plein écran plein écran.
  2. Appuyez sur Ctrl+Maj+J (ou Cmd+Option+J sur Mac) pour ouvrir les outils de développement.
  3. Cliquez sur l'onglet Réseau.
  4. Cochez la case Désactiver le cache.
  5. Actualisez l'application.

Panneau "Réseau" affichant un bundle JavaScript de 71,2 Ko.

71,2 Ko de JavaScript juste pour trier quelques nombres dans une application simple. What gives?

Dans le code source (src/index.js), la bibliothèque lodash est importée et utilisée dans cette application. Lodash fournit de nombreuses fonctions utilitaires utiles, mais une seule méthode du package est utilisée ici. Une erreur courante consiste à installer et à importer des dépendances tierces entières alors que seule une petite partie est utilisée.

Optimiser

Il existe plusieurs façons de réduire la taille du bundle :

  1. Écrire une méthode de tri personnalisée au lieu d'importer une bibliothèque tierce
  2. Utilisez la méthode Array.prototype.sort() intégrée pour trier les valeurs numériques.
  3. N'importez que la méthode sortBy à partir de lodash, et non la bibliothèque entière.
  4. Télécharger le code pour le tri uniquement lorsque l'utilisateur clique sur le bouton

Les options 1 et 2 sont des méthodes parfaitement adaptées pour réduire la taille du bundle (et seraient probablement les plus judicieuses pour une application réelle). Cependant, elles ne sont pas utilisées dans ce tutoriel à des fins pédagogiques 😈.

Les options 3 et 4 permettent d'améliorer les performances de cette application. Les prochaines sections de cet atelier de programmation couvrent ces étapes. Comme pour tout tutoriel de programmation, essayez toujours d'écrire le code vous-même au lieu de le copier et de le coller.

N'importez que ce dont vous avez besoin

Quelques fichiers doivent être modifiés pour n'importer que la méthode unique à partir de lodash. Pour commencer, remplacez cette dépendance dans package.json :

"lodash": "^4.7.0",

par ce qui suit :

"lodash.sortby": "^4.7.0",

Dans src/index.js, importez ce module spécifique :

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

Mettez également à jour la façon dont les valeurs sont triées :

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

Rechargez l'application, ouvrez les outils de développement et examinez à nouveau le panneau Réseau.

Panneau &quot;Réseau&quot; affichant un bundle JavaScript de 15,2 Ko.

Pour cette application, la taille du bundle a été réduite de plus de quatre fois avec très peu d'efforts, mais il est encore possible de l'améliorer.

Fractionnement du code

webpack est l'un des bundlers de modules Open Source les plus populaires actuellement. En bref, il regroupe tous les modules JavaScript (ainsi que d'autres éléments) qui composent une application Web dans des fichiers statiques pouvant être lus par le navigateur.

Le bundle unique utilisé dans cette application peut être divisé en deux blocs distincts :

  • Responsable du code qui constitue notre route initiale
  • Un bloc secondaire contenant notre code de tri

Grâce aux importations dynamiques, un bloc secondaire peut être chargé de manière différée ou à la demande. Dans cette application, le code qui constitue le bloc ne peut être chargé que lorsque l'utilisateur appuie sur le bouton.

Commencez par supprimer l'importation de premier niveau pour la méthode de tri dans src/index.js :

import sortBy from "lodash.sortby";

Importez-le dans l'écouteur d'événements qui se déclenche lorsque l'utilisateur appuie sur le bouton :

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

La fonctionnalité import() fait partie d'une proposition (actuellement à l'étape 3 du processus TC39) visant à inclure la possibilité d'importer dynamiquement un module. webpack a déjà inclus la prise en charge de cette fonctionnalité et suit la même syntaxe que celle définie par la proposition.

import() renvoie une promesse. Lorsqu'elle est résolue, le module sélectionné est fourni et divisé en un bloc distinct. Une fois le module renvoyé, module.default est utilisé pour référencer l'exportation par défaut fournie par lodash. La promesse est enchaînée avec un autre .then qui appelle une méthode sortInput pour trier les trois valeurs d'entrée. À la fin de la chaîne de promesses, .catch() est utilisé pour gérer les cas où la promesse est refusée en raison d'une erreur.

La dernière chose à faire est d'écrire la méthode sortInput à la fin du fichier. Il doit s'agir d'une fonction qui renvoie une fonction qui accepte la méthode importée à partir de lodash.sortBy. La fonction imbriquée peut ensuite trier les trois valeurs d'entrée et mettre à jour le DOM.

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

Surveiller

Rechargez l'application une dernière fois et surveillez de près le panneau Network (Réseau) à nouveau. Seul un petit bundle initial est téléchargé dès que l'application se charge.

Panneau &quot;Réseau&quot; affichant un bundle JavaScript de 2,7 Ko.

Une fois que l'utilisateur appuie sur le bouton pour trier les nombres saisis, le bloc contenant le code de tri est récupéré et exécuté.

Panneau &quot;Réseau&quot; affichant un bundle JavaScript de 2,7 Ko suivi d&#39;un bundle JavaScript de 13,9 Ko.

Remarquez que les nombres sont toujours triés.

Conclusion

Le fractionnement du code et le chargement différé peuvent être des techniques extrêmement utiles pour réduire la taille du bundle initial de votre application, ce qui peut entraîner directement des temps de chargement de page beaucoup plus rapides. Toutefois, certains points importants doivent être pris en compte avant d'inclure cette optimisation dans votre application.

Chargement différé de l'UI

Lorsque vous chargez à la demande des modules de code spécifiques, il est important de tenir compte de l'expérience des utilisateurs dont la connexion réseau est plus faible. Si vous fractionnez et chargez un très grand bloc de code lorsqu'un utilisateur envoie une action, l'application peut sembler avoir cessé de fonctionner. Pensez donc à afficher un indicateur de chargement.

Chargement différé des modules de nœud tiers

Le chargement différé des dépendances tierces dans votre application n'est pas toujours la meilleure approche. Cela dépend de l'endroit où vous les utilisez. En règle générale, les dépendances tierces sont divisées en un bundle vendor distinct qui peut être mis en cache, car elles ne sont pas mises à jour aussi souvent. Découvrez comment le SplitChunksPlugin peut vous aider à le faire.

Chargement différé avec un framework JavaScript

De nombreux frameworks et bibliothèques populaires qui utilisent webpack fournissent des abstractions pour faciliter le chargement différé par rapport à l'utilisation d'importations dynamiques au milieu de votre application.

Bien qu'il soit utile de comprendre le fonctionnement des importations dynamiques, utilisez toujours la méthode recommandée par votre framework/bibliothèque pour charger des modules spécifiques de manière différée.

Préchargement et prélecture

Dans la mesure du possible, profitez des indications du navigateur telles que <link rel="preload"> ou <link rel="prefetch"> pour essayer de charger les modules critiques encore plus tôt. webpack prend en charge les deux indications grâce à l'utilisation de commentaires magiques dans les instructions d'importation. Pour en savoir plus, consultez le guide Précharger les blocs critiques.

Chargement différé au-delà du code

Les images peuvent représenter une part importante d'une application. Le chargement différé des éléments situés sous la ligne de flottaison ou en dehors de la fenêtre d'affichage de l'appareil peut accélérer un site Web. Pour en savoir plus, consultez le guide Lazysizes.