Tree Shaking est un mécanisme de suppression du code inutilisé (dead code elimination) lors de l’étape de construction de l’application. Tree Shaking analyse la structure statique des modules ES et exclut les fonctions, classes et variables exportées qui ne sont importées nulle part. Selon la Documentation Webpack, une configuration correcte de Tree Shaking peut réduire la taille du bundle de 30 à 60 % sans modifier les fonctionnalités de l’application.
Points clés
Tree Shaking est une technique d’optimisation de code qui exclut les modules et fonctions inutilisés du bundle final. Le terme a été introduit par l’équipe Rollup en 2015 et décrit métaphoriquement le processus : l’arbre des dépendances est secoué et les branches inutilisées tombent. Contrairement à l’optimisation manuelle, Tree Shaking s’exécute automatiquement lors de l’étape de construction.
Tree Shaking fonctionne uniquement avec les modules ES (ECMAScript Modules), où les dépendances sont déterminées statiquement via import et export. CommonJS (require/module.exports) ne supporte pas Tree Shaking car require s’exécute dynamiquement — le bundler ne peut pas déterminer à l’avance quelles fonctions sont réellement utilisées. Les bibliothèques modernes (Lodash, Moment.js, RxJS) publient des versions ES pour supporter Tree Shaking.
Rollup a été le premier bundler à implémenter Tree Shaking en 2015. Contrairement à Webpack, Rollup a été conçu dès le départ pour les modules ES et effectue une suppression plus agressive du code mort. Rollup analyse non seulement les exportations individuelles mais des modules entiers : si un module n’a pas d’effets de bord et qu’aucune exportation n’est utilisée, Rollup exclut le module entier du bundle.
Rollup est particulièrement efficace pour les bibliothèques et SDK où chaque kilooctet compte. Le framework Vue.js utilise Rollup pour construire sa version de production. React est passé à Rollup en 2020. Pour les applications, Webpack est plus couramment utilisé en raison de son écosystème de plugins plus riche (Hot Module Replacement, code splitting, CSS modules), mais pour un Tree Shaking maximal lors de la construction de bibliothèques, Rollup reste la norme de l’industrie.
Les économies de Tree Shaking dépendent fortement de l’architecture du projet. Dans une application React utilisant la bibliothèque Ant Design, Tree Shaking peut supprimer jusqu’à 70 % du code des composants d’interface. Dans un projet où toutes les importations sont spécifiques et ciblées, les économies seront de 5 à 15 %. Selon les recherches de Webpack, les économies moyennes sont de 30 à 40 % de la taille du bundle.
| Type de code mort | Exemple | Détection Tree Shaking |
|---|---|---|
| Exportation inutilisée | export function unusedHelper() | Oui |
| Importation inutilisée | import { unused } from "lib" | Oui |
| Branche de condition morte | if (false) { ... } | Non (supprimée par le minifieur) |
| Fonction non appelée après DCE | function a(){} a() où a n’est pas appelée | Partiellement |
Le mécanisme de Tree Shaking repose sur un graphe de dépendances que le bundler construit à partir de toutes les déclarations import/export du projet. Dans la première étape, le bundler parcourt tous les fichiers depuis le point d’entrée et collecte l’arbre des modules. Dans la deuxième étape, il analyse quelles exportations de chaque module sont réellement importées dans d’autres modules.
Pour chaque module, Webpack ou Rollup marque les exportations comme utilisées ou inutilisées. Les exportations inutilisées sont exclues du bundle. Cependant, le module lui-même reste dans le bundle si au moins une de ses exportations est utilisée. Un module ne peut être complètement exclu que via le drapeau sideEffects ou si le module ne contient aucun effet de bord.
// utils.js — module avec fonctions
export function formatDate(date) {
return date.toISOString().slice(0, 10);
}
export function formatCurrency(amount) {
return "$" + amount.toFixed(2);
}
export function slugify(text) {
return text.toLowerCase().replace(/\s+/g, "-");
}// app.js — point d’entrée
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// Après Tree Shaking — seulement formatDate dans le bundle
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking a exclu formatCurrency et slugify du bundle final car ils ne sont pas importés dans app.js. La taille du module utils.js est passée de 3 fonctions à 1. Si utils.js contient des effets de bord (par exemple, une initialisation globale), Tree Shaking ne peut pas supprimer même les exportations inutilisées.
Webpack inclut un support intégré de Tree Shaking via TerserPlugin en mode production. Pour activer Tree Shaking, deux conditions sont suffisantes : mode est défini sur production (mode : "production") et les modules utilisent la syntaxe ES (import/export). Webpack marque automatiquement les exportations inutilisées et les transmet à Terser pour suppression.
La configuration supplémentaire de usedExports : true dans optimization.webpack.config.js permet une analyse détaillée de l’utilisation des exportations au sein d’un module. Cette option détermine quelles exportations sont réellement utilisées et lesquelles sont seulement exportées (provided). La combinaison de usedExports et Terser offre une efficacité maximale de suppression du code mort.
// webpack.config.js — configuration Tree Shaking
module.exports = {
mode: "production",
entry: "./src/app.js",
output: {
filename: "bundle.js",
},
optimization: {
usedExports: true,
minimize: true,
concatenateModules: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!(my-lib)\/).*/,
use: {
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", { modules: false }],
],
},
},
},
],
},
};Le paramètre clé est modules : false dans @babel/preset-env. Par défaut, Babel transforme les modules ES en CommonJS, ce qui tue Tree Shaking. modules : false empêche Babel de transformer import/export, préservant la syntaxe ES pour Webpack. concatenateModules fusionne en outre les modules dans une portée partagée, réduisant le nombre d’IIFE et diminuant la taille du bundle.
Les side effects (effets de bord) sont des actions qu’un module effectue lors de son importation et qui ne sont pas liées aux valeurs exportées : styles globaux (import "./styles.css"), polyfills (import "core-js/stable"), initialisation de variables globales ou enregistrement de Service Worker. Si un module contient des effets de bord, le bundler ne peut pas le supprimer du bundle en toute sécurité, même si aucune de ses exportations n’est utilisée.
Le drapeau sideEffects dans package.json indique au bundler quels modules du paquet n’ont pas d’effets de bord. Pour un paquet où tous les modules sont purs (uniquement des exportations de fonctions), vous devez spécifier « sideEffects : false ». Pour les paquets avec CSS ou polyfills — un tableau de chemins vers des fichiers avec effets de bord : « sideEffects : [« *.css »]». Sans ce drapeau, Tree Shaking ne supprimera même pas les fonctions inutilisées.
Pour vérifier si un module a des effets de bord, posez-vous cette question : cette importation effectuera-t-elle des actions non liées à l’exportation de valeurs ? import "./styles.css" ajoute du CSS au DOM — c’est un effet de bord. import { throttle } from "lodash-es" n’a pas d’effets de bord — il rend simplement la fonction throttle disponible. Les polyfills (import "core-js/stable") ont des effets de bord — ils modifient les prototypes globaux.
Pour vos propres modules, il est recommandé de : extraire les styles et polyfills dans des points d’entrée séparés, séparer les utilitaires purs (fonctions sans effets de bord) des modules avec effets de bord (initialisation, journalisation, enregistrement de Service Worker). Dans le package.json du projet de niveau supérieur, spécifiez « sideEffects : false » uniquement si tous les modules sont purs. S’il y a des styles, spécifiez « sideEffects : [« *.css »]» avec précision.
{
"name": "my-ui-lib",
"version": "2.1.0",
"sideEffects": [
"*.css",
"polyfills.js"
],
"module": "dist/index.esm.js",
"main": "dist/index.cjs.js"
}« sideEffects : [« *.css », « polyfills.js »]» signifie : tous les fichiers CSS ont des effets de bord (ils ne peuvent pas être supprimés) et polyfills.js aussi. Tous les autres fichiers JS du paquet sont purs — ils peuvent être secoués en toute sécurité. Le champ module spécifie le chemin vers la version ES du paquet que le bundler doit utiliser à la place de la version CommonJS (main) pour Tree Shaking.
React Native avec Metro Bundler supporte une version limitée de Tree Shaking. Metro n’effectue pas d’analyse statique complète des exportations utilisées (usedExports) comme Webpack. Au lieu de cela, Metro se repose sur Terser pour supprimer les parties inutilisées des modules lors de la minification. L’efficacité de cette approche est inférieure à celle de Tree Shaking complet dans Webpack.
Pour une optimisation maximale des projets React Native, il est recommandé d’utiliser des bibliothèques avec des modules ES (champ module dans package.json), d’ajouter babel-plugin-transform-remove-console pour supprimer le code de débogage, et de configurer Metro transformer.minifierConfig pour Terser. De plus, Ram Bundle (division du bundle en modules) réduit le chargement des écrans inutilisés.
Questions fréquemment posées
CommonJS (require/module.exports) ne supporte pas l’analyse statique — require peut être appelé dynamiquement dans des conditions et fonctions. Le bundler ne peut pas déterminer quelles parties du module sont réellement utilisées. Seuls les modules ES avec import/export statique permettent Tree Shaking.
TypeScript est totalement compatible avec Tree Shaking à condition que tsconfig.json soit configuré pour les modules ES : « module : « esnext »». Le compilateur TypeScript doit préserver import/export sans les convertir en CommonJS. Babel avec @babel/preset-typescript et modules : false transmet également correctement les modules ES à Webpack.
Webpack Bundle Analyzer est un plugin qui visualise la composition du bundle sous forme de diagramme interactif. Si une bibliothèque est présente dans le bundle mais que ses fonctions ne sont pas utilisées, Tree Shaking n’a pas fonctionné. Vous pouvez également analyser le fichier de sortie : recherchez les exportations inutilisées dans le texte du bundle via grep.
Lodash v4 est distribué comme un paquet CommonJS. Pour Tree Shaking, vous devez utiliser lodash-es — la version ES de la bibliothèque. Remplacez import throttle from "lodash/throttle" par import { throttle } from "lodash-es" et configurez resolve.alias dans Webpack pour remplacer lodash par lodash-es.
Tree Shaking augmente légèrement le temps de construction (de 5 à 15 %) car il ajoute une étape d’analyse du graphe de dépendances et de marquage des exportations utilisées. En mode développement, Tree Shaking est généralement désactivé pour la vitesse. En production, le temps supplémentaire est justifié par une réduction significative de la taille du bundle.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi