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

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

Cet atelier de programmation vous explique comment utiliser le fractionnement de 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" (Tri magique) avec trois champs de saisie de nombres et un bouton de tri.

Mesurer

Comme toujours, il est important de commencer par mesurer les performances d'un site Web avant d'essayer d'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 DevTools.
  3. Cliquez sur l'onglet Réseau.
  4. Cochez la case Disable cache (Désactiver le cache).
  5. Actualisez l'application.

Panneau "Network" (Réseau) affichant le bundle JavaScript de 71,2 ko

71,2 ko de code JavaScript 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 seule une seule méthode du package est utilisée ici. Il est courant d'installer et d'importer des dépendances tierces complètes alors qu'une petite partie seulement est utilisée.

Optimiser

Vous pouvez réduire la taille du bundle de plusieurs façons :

  1. Écrire une méthode de tri personnalisée au lieu d'importer une bibliothèque tierce
  2. Utiliser la méthode Array.prototype.sort() intégrée pour trier numériquement
  3. N'importez que la méthode sortBy à partir de lodash et non l'intégralité de la bibliothèque
  4. Télécharger le code de 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 adaptées pour une application réelle). Cependant, ils ne sont pas utilisés dans ce tutoriel à des fins pédagogiques 😈.

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

N'importez que ce dont vous avez besoin

Vous devez modifier quelques fichiers pour n'importer qu'une seule méthode à partir de lodash. Pour commencer, remplacez cette dépendance dans package.json :

"lodash": "^4.7.0",

avec ce qui suit :

"lodash.sortby": "^4.7.0",

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

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

Modifiez également l'ordre de tri des valeurs :

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>
  `
});

Actualisez l'application, ouvrez les outils de développement et consultez à nouveau le panneau Network (Réseau).

Panneau &quot;Network&quot; (Réseau) 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 reste encore des améliorations à apporter.

Division du code

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

Le seul bundle utilisé dans cette application peut être divisé en deux composants distincts:

  • L'un est responsable du code qui constitue notre parcours initial.
  • 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 chargé à la demande. Dans cette application, le code qui compose le bloc ne peut être chargé que lorsque l'utilisateur appuie sur le bouton.

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

import sortBy from "lodash.sortby";

Importez-le également 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 à permettre l'importation dynamique d'un module. Webpack est déjà compatible avec cette fonctionnalité et suit la même syntaxe que celle définie dans 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() permet de 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 exploite 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

Actualisez l'application une dernière fois et surveillez à nouveau le panneau Réseau. Seul un petit bundle initial est téléchargé dès le chargement de l'application.

Panneau &quot;Network&quot; (Réseau) affichant un bundle JavaScript de 2,7 ko.

Une fois le bouton enfoncé pour trier les nombres saisis, le bloc contenant le code de tri est extrait et exécuté.

Panneau &quot;Network&quot; affichant un bundle JavaScript de 2,7 Ko, suivi d&#39;un bundle JavaScript de 13,9 Ko

Notez que les nombres sont toujours triés.

Conclusion

La division du code et le chargement différé peuvent être des techniques extrêmement utiles pour réduire la taille initiale du bundle de votre application, et cela peut directement réduire considérablement le temps de chargement des pages. Toutefois, certains points importants doivent être pris en compte avant d'inclure cette optimisation dans votre application.

UI de chargement différé

Lorsque vous chargez de manière différée des modules de code spécifiques, il est important de réfléchir à l'expérience des utilisateurs disposant de connexions réseau plus faibles. Si vous divisez et chargez un très grand bloc de code lorsqu'un utilisateur envoie une action, il peut sembler que l'application a cessé de fonctionner. Pensez donc à afficher un indicateur de chargement.

Modules de nœuds tiers à chargement différé

Il n'est pas toujours optimal de charger de manière paresseuse les dépendances tierces dans votre application. 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 dans cette démarche.

Chargement différé avec un framework JavaScript

De nombreux frameworks et bibliothèques populaires qui utilisent webpack fournissent des abstractions pour faciliter le chargement paresseux plutôt que d'utiliser des 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 en différé.

Préchargement et préchargement

Dans la mesure du possible, exploitez les optimisations de 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 suggestions via l'utilisation de commentaires magiques dans les instructions d'importation. Pour en savoir plus, consultez le guide Précharger des blocs critiques.

Chargement différé de plus que du code

Les images peuvent constituer une part importante d'une application. Le chargement différé des éléments situés en dessous de 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.