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, le fait de diviser le code JavaScript en plusieurs fragments améliore les performances de la page.

Cet atelier de programmation explique comment utiliser la scission 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 Trieur magique avec trois champs pour la saisie de chiffres 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'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 groupe JavaScript de 71,2 Ko

71,2 Ko de JavaScript, juste pour trier quelques chiffres 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. L'installation et l'importation de dépendances tierces entières dont seule une petite partie de celles-ci est utilisée est une erreur courante.

Optimisation

La taille du lot peut être réduite de plusieurs façons:

  1. Écrivez 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 effectuer un tri numérique.
  3. Importez uniquement 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 parfaitement appropriées pour réduire la taille du bundle (et seraient probablement la plus logique pour une application réelle). Toutefois, ils ne sont pas utilisés dans ce tutoriel à des fins d'enseignement RoleBinding.

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

Importez uniquement ce dont vous avez besoin

Vous devez modifier quelques fichiers pour n'importer que la méthode unique depuis lodash. Pour commencer, remplacez cette dépendance dans package.json:

"lodash": "^4.7.0",

par:

"lodash.sortby": "^4.7.0",

À présent, dans src/index.js, importez ce module spécifique:

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

Modifiez le mode 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, puis regardez à nouveau le panneau Network (Réseau).

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

Pour cette application, la taille du bundle a été réduite de plus de quatre fois en peu de travail, mais la marge de progression reste encore élevée.

Fractionnement du code

webpack est l'un des bundlers de modules Open Source les plus populaires utilisés aujourd'hui. En bref, il regroupe tous les modules JavaScript (ainsi que d'autres éléments) qui constituent une application Web dans des fichiers statiques lisibles par le navigateur.

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

  • Un responsable du code qui constitue notre itinéraire initial
  • Un bloc secondaire qui contient notre code de tri

Grâce aux importations dynamiques, un fragment secondaire peut être chargé de manière différée ou à la demande. Dans cette application, le code qui compose le fragment 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 est déjà compatible avec cette fonctionnalité et suit la même syntaxe décrite par la proposition.

import() renvoie une promesse. Lorsqu'elle est résolue, le module sélectionné est fourni, qui est divisé en un fragment distinct. Une fois le module renvoyé, module.default permet de référencer l'exportation par défaut fournie par lodash. La promesse est enchaînée à 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 rejetée en raison d'une erreur.

La dernière étape consiste à écrire la méthode sortInput à la fin du fichier. Il doit s'agir d'une fonction qui renvoie une fonction qui utilise 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 de près le panneau Network (Réseau). Seul un petit bundle initial est téléchargé dès le chargement de l'application.

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

Une fois que vous avez appuyé sur le bouton pour trier les numéros d'entrée, le fragment contenant le code de tri est récupéré et exécuté.

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

Notez que les chiffres sont toujours triés.

Conclusion

Le fractionnement de code et le chargement différé peuvent être des techniques extrêmement utiles pour réduire la taille initiale du bundle de votre application. Cela peut directement réduire le temps de chargement des pages. Toutefois, vous devez prendre en compte certains points importants avant d'inclure cette optimisation dans votre application.

UI de chargement différé

Lors du chargement différé de modules de code spécifiques, il est important de prendre en compte l'expérience des utilisateurs dont les connexions réseau sont plus faibles. La division et le chargement d'un très grand bloc de code lorsqu'un utilisateur envoie une action peut donner l'impression que l'application a peut-être cessé de fonctionner. Envisagez donc d'afficher un indicateur de chargement.

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

Ce n'est pas toujours la meilleure approche pour effectuer un chargement différé des dépendances tierces dans votre application, et 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 SplitChunksPlugin peut vous aider à y parvenir.

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 effectuer le chargement différé de modules spécifiques.

Préchargement et préchargement

Dans la mesure du possible, profitez des optimisations du navigateur telles que <link rel="preload"> ou <link rel="prefetch"> pour essayer de charger des modules critiques encore plus rapidement. Webpack prend en charge ces deux suggestions grâce à l'utilisation de commentaires magiques dans les instructions d'importation. Cela est expliqué plus en détail dans le guide Précharger les fragments critiques.

Le chargement différé ne se limite pas au code

Les images peuvent constituer une partie importante d'une application. Le chargement différé des éléments qui se trouvent 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 à ce sujet, consultez le guide Lazysizes.