Tree shaking : retirer du bundle le code que personne n'importe

Le tree shaking supprime du fichier final les exports qu'aucun module n'utilise. Voici pourquoi il exige des modules ES, et ce qui l'empêche de fonctionner.
3 min de lecture
Believemy logo

Vous importez une seule fonction d'une bibliothèque qui en propose deux cents. Les cent quatre-vingt-dix-neuf autres n'ont aucune raison de voyager jusqu'au navigateur du visiteur.

Le tree shaking sert exactement à cela : secouer l'arbre des dépendances et laisser tomber les branches mortes.


Définition

Le tree shaking est l'élimination, au moment du build, du code exporté qu'aucun fichier n'importe. Un Bundler (empaqueteur) construit le graphe des imports, marque ce qui est réellement atteint, puis écrit le fichier final sans le reste.

JAVASCRIPT
// outils.js
export function formaterPrix(valeur) {
  return valeur.toFixed(2) + " EUR";
}

export function genererFacturePdf(commande) {
  // 40 Ko de dépendances mobilisées ici
  return commande;
}

// main.js
import { formaterPrix } from './outils.js';

console.log(formaterPrix(12.5)); // 12.50 EUR

// Dans le fichier produit : genererFacturePdf a disparu,
// et ses 40 Ko de dépendances avec elle.

Le gain vient rarement de votre propre code. Il vient des dépendances, où un import mal ciblé embarque une bibliothèque entière pour trois lignes utiles.

Le poids envoyé au navigateur avant et après le tree shakingAvant le build, 42 Ko dont 40 pour une fonction que personne n'importe. Après, il reste 2 Ko, soit 21 fois moins.Ce que le build garde, ce qu'il abandonneAvant le build : 42 Ko avec les dépendancesgenererFacturePdf et ses 40 Kopersonne ne l'importe : abandonnéLe bundler garde ce qui est importé, retire le resteAprès le build : 2 Ko dans le bundleformaterPrix, la seule fonction importée21 fois moins de code envoyé au navigateur


Pourquoi cela n'est possible qu'avec les modules ES

Un Module ES déclare ses imports et ses exports de façon fixe, en tête de fichier, sans condition. L'outil peut donc décider avant toute exécution ce qui est utilisé.

Avec CommonJS, require() est un appel ordinaire, dont le chemin peut être calculé et le résultat modifié en cours de route. Aucune analyse fiable n'est possible, donc rien n'est retiré. C'est l'argument technique le plus fort en faveur des modules ES sur du code neuf.

Bon à savoir

Un import de la forme import * as tout from './outils.js' suivi d'un accès dynamique par variable rend l'analyse impossible : le bundler ne peut pas savoir quelle propriété sera lue, donc il garde tout.


Ce qui empêche l'élagage

  • Les effets de bord : un module qui, au chargement, modifie un objet global ou installe un écouteur ne peut pas être retiré sans risque, même si aucun de ses exports n'est utilisé.
  • Le doute : dans l'incertitude, un bundler conserve. Sa priorité est de ne rien casser, pas de gagner un kilo-octet.
  • Une déclaration manquante : le champ "sideEffects": false d'un package.json annonce qu'un paquet peut être élagué sans crainte. Sans lui, l'outil reste prudent.


Questions fréquentes

Question

Faut-il activer le tree shaking ?

Il l'est déjà dans la configuration de production des outils courants. La vraie question porte sur les conditions à réunir pour qu'il opère : des modules ES, des imports nommés, et des dépendances distribuées dans un format analysable.


Question

Comment vérifier qu'il a fonctionné ?

En regardant le rapport de composition du bundle, que tous les empaqueteurs savent produire. Il montre le poids réel de chaque module dans le fichier final. Chercher au hasard le nom d'une fonction dans le fichier minifié ne prouve rien, puisque les noms internes ont changé.


Question

Pourquoi une bibliothèque reste-t-elle entière dans mon bundle ?

Trois causes principales : elle n'est publiée qu'en CommonJS, elle est importée en bloc plutôt que par exports nommés, ou elle exécute du code au chargement. Beaucoup de paquets proposent une version en modules ES : c'est celle qu'il faut viser.

Termes connexes

Découvrez notre glossaire JavaScript

Tous les mots de JavaScript expliqués simplement : mots-clés, objets natifs, méthodes, erreurs et concepts. Définitions claires et exemples qui tournent, pour apprendre et pour se dépanner.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.