Tree Shaking ist ein Mechanismus zur Entfernung von ungenutztem Code (Dead Code Elimination) während der Build-Phase der Anwendung. Tree Shaking analysiert die statische Struktur von ES-Modulen und schließt exportierte Funktionen, Klassen und Variablen aus, die nirgendwo importiert werden. Laut der Webpack-Dokumentation kann eine korrekte Tree-Shaking-Konfiguration die Bündelgröße um 30–60% reduzieren, ohne die Anwendungsfunktionalität zu ändern.
Wichtige Punkte
Tree Shaking ist eine Code-Optimierungstechnik, die ungenutzte Module und Funktionen aus dem endgültigen Bündel ausschließt. Der Begriff wurde 2015 vom Rollup-Team eingeführt und beschreibt den Prozess metaphorisch: Der Abhängigkeitsbaum wird geschüttelt, und ungenutzte Zweige fallen ab. Im Gegensatz zur manuellen Optimierung wird Tree Shaking automatisch während der Build-Phase ausgeführt.
Tree Shaking funktioniert nur mit ES-Modulen (ECMAScript Modules), bei denen Abhängigkeiten statisch durch import und export bestimmt werden. CommonJS (require/module.exports) unterstützt Tree Shaking nicht, da require dynamisch ausgeführt wird — der Bündler kann nicht im Voraus bestimmen, welche Funktionen tatsächlich verwendet werden. Moderne Bibliotheken (Lodash, Moment.js, RxJS) veröffentlichen ES-Versionen, um Tree Shaking zu unterstützen.
Rollup war der erste Bündler, der Tree Shaking im Jahr 2015 implementierte. Im Gegensatz zu Webpack wurde Rollup von Grund auf für ES-Module entwickelt und führt eine aggressivere Entfernung von totem Code durch. Rollup analysiert nicht nur einzelne Exporte, sondern ganze Module: Wenn ein Modul keine Nebeneffekte hat und kein Export verwendet wird, schließt Rollup das gesamte Modul aus dem Bündel aus.
Rollup ist besonders effektiv für Bibliotheken und SDKs, bei denen jedes Kilobyte zählt. Das Vue.js-Framework verwendet Rollup für die Erstellung seiner Produktionsversion. React ist 2020 auf Rollup umgestiegen. Für Anwendungen wird Webpack aufgrund seines reichhaltigeren Plugin-Ökosystems (Hot Module Replacement, Code Splitting, CSS Modules) häufiger verwendet, aber für maximales Tree Shaking beim Erstellen von Bibliotheken bleibt Rollup der Industriestandard.
Die Einsparungen durch Tree Shaking hängen stark von der Projektarchitektur ab. In einer React-Anwendung mit der Bibliothek Ant Design kann Tree Shaking bis zu 70% des Codes der UI-Komponenten entfernen. In einem Projekt, in dem alle Importe spezifisch und gezielt sind, betragen die Einsparungen 5–15%. Laut Webpack-Forschung liegen die durchschnittlichen Einsparungen bei 30–40% der Bündelgröße.
| Art von totem Code | Beispiel | Tree-Shaking-Erkennung |
|---|---|---|
| Ungenutzter Export | export function unusedHelper() | Ja |
| Ungenutzter Import | import { unused } from "lib" | Ja |
| Toter Bedingungszweig | if (false) { ... } | Nein (vom Minifier entfernt) |
| Nicht aufgerufene Funktion nach DCE | function a(){} a() wobei a nicht aufgerufen wird | Teilweise |
Der Tree-Shaking-Mechanismus basiert auf einem Abhängigkeitsgraphen, den der Bündler aus allen import/export-Anweisungen im Projekt erstellt. In der ersten Phase durchläuft der Bündler alle Dateien vom Einstiegspunkt aus und sammelt den Modulbaum. In der zweiten Phase analysiert er, welche Exporte aus jedem Modul tatsächlich in anderen Modulen importiert werden.
Für jedes Modul markiert Webpack oder Rollup Exporte als verwendet oder unverwendet. Unverwendete Exporte werden aus dem Bündel ausgeschlossen. Das Modul selbst bleibt jedoch im Bündel, wenn mindestens einer seiner Exporte verwendet wird. Ein Modul kann nur durch das sideEffects-Flag oder wenn das Modul keine Nebeneffekte enthält, vollständig ausgeschlossen werden.
// utils.js — Modul mit Funktionen
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 — Einstiegspunkt
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// Nach Tree Shaking — im Bündel nur formatDate
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking hat formatCurrency und slugify aus dem endgültigen Bündel ausgeschlossen, da sie in app.js nicht importiert werden. Die Größe des Moduls utils.js wurde von 3 Funktionen auf 1 reduziert. Wenn utils.js Nebeneffekte enthält (z. B. globale Initialisierung), kann Tree Shaking selbst ungenutzte Exporte nicht entfernen.
Webpack enthält integrierte Tree-Shaking-Unterstützung über den TerserPlugin im Produktionsmodus. Um Tree Shaking zu aktivieren, sind zwei Bedingungen ausreichend: mode ist auf production gesetzt (mode: "production") und Module verwenden ES-Syntax (import/export). Webpack markiert automatisch ungenutzte Exporte und übergibt sie an Terser zur Entfernung.
Die zusätzliche Konfiguration von usedExports: true in optimization.webpack.config.js ermöglicht eine detaillierte Analyse der Exportnutzung innerhalb eines Moduls. Diese Option bestimmt, welche Exporte tatsächlich verwendet werden und welche nur exportiert (provided) werden. Die Kombination von usedExports und Terser bietet die maximale Effizienz bei der Entfernung von totem Code.
// webpack.config.js — Tree Shaking Konfiguration
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 }],
],
},
},
},
],
},
};Der Schlüsselparameter ist modules: false in @babel/preset-env. Standardmäßig wandelt Babel ES-Module in CommonJS um, was Tree Shaking zerstört. modules: false verhindert, dass Babel import/export umwandelt, und bewahrt die ES-Syntax für Webpack. concatenateModules führt Module zusätzlich in einem gemeinsamen Bereich zusammen, reduziert die Anzahl der IIFEs und verringert die Bündelgröße.
Side Effects (Nebeneffekte) sind Aktionen, die ein Modul beim Importieren ausführt und die nicht mit den exportierten Werten zusammenhängen: globale Styles (import "./styles.css"), Polyfills (import "core-js/stable"), Initialisierung globaler Variablen oder Service-Worker-Registrierung. Wenn ein Modul Nebeneffekte enthält, kann der Bündler es nicht sicher aus dem Bündel entfernen, selbst wenn keiner seiner Exporte verwendet wird.
Das sideEffects-Flag in package.json teilt dem Bündler mit, welche Module im Paket keine Nebeneffekte haben. Für ein Paket, in dem alle Module rein sind (nur Funktions-Exporte), sollten Sie „sideEffects“: false angeben. Für Pakete mit CSS oder Polyfills — ein Array von Pfaden zu Dateien mit Nebeneffekten: „sideEffects“: [„*.css“]. Ohne dieses Flag entfernt Tree Shaking nicht einmal ungenutzte Funktionen.
Um zu überprüfen, ob ein Modul Nebeneffekte hat, stellen Sie sich diese Frage: Wird dieser Import irgendwelche Aktionen ausführen, die nichts mit dem Exportieren von Werten zu tun haben? import "./styles.css" fügt CSS zum DOM hinzu — das ist ein Nebeneffekt. import { throttle } from "lodash-es" hat keine Nebeneffekte — es macht lediglich die Funktion throttle verfügbar. Polyfills (import "core-js/stable") haben Nebeneffekte — sie modifizieren globale Prototypen.
Für eigene Module wird empfohlen: Styles und Polyfills in separate Einstiegspunkte auslagern, reine Dienstprogramme (Funktionen ohne Nebeneffekte) von Modulen mit Nebeneffekten (Initialisierung, Protokollierung, Service-Worker-Registrierung) trennen. In der package.json des übergeordneten Projekts „sideEffects“: false nur angeben, wenn alle Module rein sind. Wenn es Styles gibt, „sideEffects“: [„*.css“] präzise angeben.
{
"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“] bedeutet: Alle CSS-Dateien haben Nebeneffekte (sie können nicht entfernt werden) und polyfills.js ebenfalls. Alle anderen JS-Dateien im Paket sind rein — sie können sicher geschüttelt werden. Das Feld module gibt den Pfad zur ES-Version des Pakets an, die der Bündler anstelle der CommonJS-Version (main) für Tree Shaking verwenden soll.
React Native mit Metro Bundler unterstützt eine eingeschränkte Version von Tree Shaking. Metro führt keine vollständige statische Analyse der verwendeten Exporte (usedExports) wie Webpack durch. Stattdessen verlässt sich Metro auf Terser, um ungenutzte Teile von Modulen während der Minifizierung zu entfernen. Die Effektivität dieses Ansatzes ist geringer als vollständiges Tree Shaking in Webpack.
Für eine maximale Optimierung von React Native-Projekten wird empfohlen: Bibliotheken mit ES-Modulen verwenden (Feld module in package.json), babel-plugin-transform-remove-console zum Entfernen von Debug-Code hinzufügen und Metro transformer.minifierConfig für Terser konfigurieren. Zusätzlich reduziert Ram Bundle (Aufteilung des Bündels in Module) das Laden ungenutzter Bildschirme.
Häufig gestellte Fragen
CommonJS (require/module.exports) unterstützt keine statische Analyse — require kann dynamisch innerhalb von Bedingungen und Funktionen aufgerufen werden. Der Bündler kann nicht bestimmen, welche Teile des Moduls tatsächlich verwendet werden. Nur ES-Module mit statischem import/export ermöglichen Tree Shaking.
TypeScript ist vollständig kompatibel mit Tree Shaking, sofern tsconfig.json für ES-Module konfiguriert ist: „module“: „esnext“. Der TypeScript-Compiler muss import/export erhalten, ohne sie in CommonJS umzuwandeln. Babel mit @babel/preset-typescript und modules: false übergibt ES-Module ebenfalls korrekt an Webpack.
Webpack Bundle Analyzer ist ein Plugin, das die Bündelzusammensetzung als interaktives Diagramm visualisiert. Wenn eine Bibliothek im Bündel vorhanden ist, aber ihre Funktionen nicht verwendet werden, hat Tree Shaking nicht funktioniert. Sie können auch die Ausgabedatei analysieren: Suchen Sie mit grep nach ungenutzten Exporten im Bündeltext.
Lodash v4 wird als CommonJS-Paket vertrieben. Für Tree Shaking müssen Sie lodash-es verwenden — die ES-Version der Bibliothek. Ersetzen Sie import throttle from "lodash/throttle" durch import { throttle } from "lodash-es" und konfigurieren Sie resolve.alias in Webpack, um lodash durch lodash-es zu ersetzen.
Tree Shaking erhöht die Build-Zeit geringfügig (um 5–15%), da es eine Phase der Abhängigkeitsgraphen-Analyse und Markierung verwendeter Exporte hinzufügt. Im Entwicklungsmodus ist Tree Shaking in der Regel aus Geschwindigkeitsgründen deaktiviert. In der Produktion wird die zusätzliche Zeit durch eine deutliche Reduzierung der Bündelgröße gerechtfertigt.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch