Tree Shaking: co to jest, mechanizm usuwania martwego kodu i narzędzia

Autor: IT Sectr Opublikowano: 2026-05-18 Czas czytania: 8 min

Tree Shaking — mechanizm usuwania nieużywanego kodu (dead code elimination) na etapie budowania aplikacji. Tree Shaking analizuje statyczną strukturę modułów ES i wyklucza eksportowane funkcje, klasy i zmienne, które nie są nigdzie importowane. Według danych Webpack Documentation, prawidłowa konfiguracja Tree Shaking może zmniejszyć rozmiar pakietu o 30–60% bez zmiany funkcjonalności aplikacji.

Najważniejsze

  • Tree Shaking — usuwanie nieużywanych eksportów z modułów ES na podstawie statycznej analizy import/export
  • Moduły ES (import/export) — jedyny format obsługujący Tree Shaking; CommonJS nie jest obsługiwany
  • Webpack i Rollup — główne narzędzia do budowania z obsługą Tree Shaking przez wtyczki
  • Side effects — efekty uboczne w modułach blokują Tree Shaking; flaga sideEffects: false w package.json rozwiązuje problem
  • Used exports — analiza użycia eksportów w trybie produkcyjnym Webpack do precyzyjnego usuwania martwego kodu

Czym jest Tree Shaking?

Tree Shaking (wytrząsanie drzewa) — technika optymalizacji kodu, w której z finalnego pakietu wykluczane są moduły i funkcje nieużywane w aplikacji. Termin został wprowadzony przez zespół Rollup w 2015 roku i metaforycznie opisuje proces: drzewo zależności jest wytrząsane, a nieużywane gałęzie odpadają. W przeciwieństwie do ręcznej optymalizacji, Tree Shaking jest wykonywany automatycznie na etapie budowania.

Tree Shaking działa tylko z modułami ES (ECMAScript Modules), gdzie zależności są określane statycznie przez import i export. CommonJS (require/module.exports) nie obsługuje Tree Shaking, ponieważ require jest wykonywany dynamicznie — narzędzie budujące nie może z góry określić, które funkcje są faktycznie używane. Nowoczesne biblioteki (Lodash, Moment.js, RxJS) wydają wersje ES do obsługi Tree Shaking.

Rollup: pionier Tree Shaking

Rollup — pierwsze narzędzie budujące, które wdrożyło Tree Shaking w 2015 roku. W przeciwieństwie do Webpack, Rollup od początku był projektowany dla modułów ES i wykonuje bardziej agresywne usuwanie martwego kodu. Rollup analizuje nie tylko poszczególne eksporty, ale także całe moduły: jeśli moduł nie ma efektów ubocznych i żaden eksport nie jest używany, Rollup wyklucza cały moduł z pakietu.

Rollup jest szczególnie efektywny dla bibliotek i SDK, gdzie każdy kilobajt ma znaczenie. Framework Vue.js używa Rollup do budowania wersji produkcyjnej. React przeszedł na Rollup w 2020 roku. Do aplikacji częściej używa się Webpack ze względu na bogatszą ekosystem wtyczek (Hot Module Replacement, code splitting, CSS modules), ale dla maksymalnego Tree Shaking przy budowaniu bibliotek Rollup pozostaje standardem branżowym.

Oszczędność z Tree Shaking silnie zależy od architektury projektu. W aplikacji na React z biblioteką Ant Design Tree Shaking może usunąć do 70% kodu komponentów UI. W projekcie, gdzie wszystkie importy są specyficzne i punktowe, oszczędność wyniesie 5–15%. Średnia oszczędność według badań Webpack wynosi 30–40% rozmiaru pakietu.

Czym jest martwy kod

Typ martwego koduPrzykładWykrywanie przez Tree Shaking
Nieużywany eksportexport function unusedHelper()Tak
Nieużywany importimport { unused } from "lib"Tak
Martwa gałąź warunkuif (false) { ... }Nie (usuwane przez minifikator)
Niewywołana funkcja po DCEfunction a(){} a() gdzie a nie jest wywołaneCzęściowo

Jak działa Tree Shaking: statyczna analiza modułów

Mechanizm Tree Shaking opiera się na grafie zależności (dependency graph), który narzędzie budujące tworzy ze wszystkich import/export w projekcie. W pierwszym etapie narzędzie budujące przegląda wszystkie pliki od punktu wejścia (entry point) i zbiera drzewo modułów. W drugim etapie analizowane jest, które eksporty z każdego modułu są faktycznie importowane w innych modułach.

Dla każdego modułu Webpack lub Rollup oznacza eksporty jako używane lub nieużywane. Nieużywane eksporty są wykluczane z pakietu. Jednak sam moduł pozostaje w pakiecie, jeśli przynajmniej jeden jego eksport jest używany. Całkowicie wykluczyć moduł można tylko przez flagę sideEffects lub jeśli moduł nie zawiera żadnych efektów ubocznych.

Przykład: przed i po Tree Shaking

js
// utils.js — moduł z funkcjami
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, "-");
}
js
// app.js — punkt wejścia
import { formatDate } from "./utils";

const today = formatDate(new Date());
console.log(today);
js
// Po Tree Shaking — w pakiecie tylko formatDate
function formatDate(date) {
  return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);

Tree Shaking wykluczył formatCurrency i slugify z finalnego pakietu, ponieważ nie są importowane w app.js. Rozmiar modułu utils.js zmniejszył się z 3 funkcji do 1. Jeśli utils.js zawiera side effects (np. globalną inicjalizację), Tree Shaking nie będzie mógł usunąć nawet nieużywanych eksportów.

Tree Shaking w Webpack: konfiguracja i optymalizacja

Webpack zawiera wbudowaną obsługę Tree Shaking przez wtyczkę TerserPlugin w trybie produkcyjnym. Do włączenia Tree Shaking wystarczą dwa warunki: mode ustawiony na production (mode: "production") i moduły używające składni ES (import/export). Webpack automatycznie oznacza nieużywane eksporty i przekazuje je Terser do usunięcia.

Dodatkowa konfiguracja usedExports: true w optimization.webpack.config.js włącza szczegółową analizę użycia eksportów wewnątrz modułu. Ta opcja określa, które eksporty są faktycznie używane (used), a które są tylko eksportowane (provided). Kombinacja usedExports i Terser daje maksymalną efektywność usuwania martwego kodu.

Konfiguracja Webpack dla Tree Shaking

js
// webpack.config.js — konfiguracja 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 }],
            ],
          },
        },
      },
    ],
  },
};

Kluczowy parametr — modules: false w @babel/preset-env. Babel domyślnie przekształca moduły ES w CommonJS, co zabija Tree Shaking. modules: false zabrania Babel przekształcania import/export, zachowując składnię ES dla Webpack. concatenateModules dodatkowo łączy moduły we wspólnym zakresie widoczności, zmniejszając liczbę IIFE i rozmiar pakietu.

Problem side effects i flaga sideEffects

Side effects (efekty uboczne) — działania modułu przy jego imporcie, niezwiązane z eksportowanymi wartościami: globalne style (import "./styles.css"), polifile (import "core-js/stable"), inicjalizacja globalnych zmiennych lub rejestracja Service Worker. Jeśli moduł zawiera side effects, narzędzie budujące nie może bezpiecznie usunąć go z pakietu, nawet jeśli żaden eksport nie jest używany.

Flaga sideEffects w package.json informuje narzędzie budujące, które moduły w pakiecie nie mają efektów ubocznych. Dla pakietu, gdzie wszystkie moduły są czyste (tylko eksport funkcji), należy ustawić "sideEffects": false. Dla pakietów z CSS lub polifilami — tablicę ścieżek do plików z efektami ubocznymi: "sideEffects": ["*.css"]. Bez tej flagi Tree Shaking nie usunie nawet nieużywanych funkcji.

Jak określić side effects we własnym kodzie

Aby sprawdzić, czy w module są efekty uboczne, zadaj pytanie: czy ten import wykona jakieś działania niezwiązane z eksportem wartości? import "./styles.css" dodaje CSS do DOM — to side effect. import { throttle } from "lodash-es" nie ma side effects — tylko udostępnia funkcję throttle. Polifile (import "core-js/stable") mają side effects — modyfikują globalne prototypy.

Dla własnych modułów zaleca się: wyodrębniać style i polifile do osobnych punktów wejścia, oddzielać czyste narzędzia (funkcje bez side effects) od modułów z efektami ubocznymi (inicjalizacja, logowanie, rejestracja Service Worker). W package.json projektu głównego ustawiać "sideEffects": false tylko jeśli wszystkie moduły są czyste. Jeśli są style — ustawiać "sideEffects": ["*.css"] dokładnie.

Przykład konfiguracji sideEffects

json
{
  "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"] oznacza: wszystkie pliki CSS mają efekty uboczne (nie można ich usuwać) i polyfills.js też. Wszystkie pozostałe pliki JS w pakiecie są czyste — można je bezpiecznie shake-ować. Pole module wskazuje ścieżkę do wersji ES pakietu, której narzędzie budujące powinno używać zamiast wersji CommonJS (main) dla Tree Shaking.

Tree Shaking w React Native i Metro

React Native z Metro Bundler obsługuje ograniczoną wersję Tree Shaking. Metro nie wykonuje pełnej statycznej analizy używanych eksportów (usedExports) jak Webpack. Zamiast tego Metro polega na Terser do usuwania nieużywanej części modułów na etapie minifikacji. Efektywność takiego podejścia jest niższa niż pełny Tree Shaking w Webpack.

Dla maksymalnej optymalizacji React Native zaleca się: używać bibliotek z modułami ES (pole module w package.json), podłączać wtyczki babel-plugin-transform-remove-console do usuwania kodu debugowania i konfigurować Metro transformer.minifierConfig dla Terser. Dodatkowo Ram Bundle (podział pakietu na moduły) zmniejsza ładowanie nieużywanych ekranów.

Często zadawane pytania

Dlaczego Tree Shaking nie działa z CommonJS?

CommonJS (require/module.exports) nie obsługuje statycznej analizy — require może być wywoływany dynamicznie wewnątrz warunków i funkcji. Narzędzie budujące nie może określić, które części modułu są faktycznie używane. Tylko moduły ES ze statycznymi import/export umożliwiają Tree Shaking.

Czy można używać Tree Shaking z TypeScript?

TypeScript jest w pełni kompatybilny z Tree Shaking pod warunkiem, że tsconfig.json jest skonfigurowany na moduły ES: "module": "esnext". Kompilator TypeScript musi zachowywać import/export bez przekształcania na CommonJS. Babel z @babel/preset-typescript i modules: false również poprawnie przekazuje moduły ES do Webpack.

Jak sprawdzić, czy Tree Shaking zadziałał?

Webpack Bundle Analyzer — wtyczka wizualizująca skład pakietu w formie interaktywnego diagramu. Jeśli biblioteka jest obecna w pakiecie, ale jej funkcje nie są używane, Tree Shaking nie zadziałał. Można również przeanalizować plik wyjściowy: znaleźć nieużywany eksport w tekście pakietu przez grep.

Dlaczego Lodash nie jest tree-shake-owany domyślnie?

Lodash v4 jest dystrybuowany jako pakiet CommonJS. Do Tree Shaking należy użyć lodash-es — wersji ES biblioteki. Zamień import throttle from "lodash/throttle" na import { throttle } from "lodash-es" i skonfiguruj resolve.alias w Webpack do podmiany lodash na lodash-es.

Czy Tree Shaking wpływa na czas budowania?

Tree Shaking nieznacznie wydłuża czas budowania (o 5–15%), ponieważ dodaje etap analizy grafu zależności i oznaczania używanych eksportów. W trybie development Tree Shaking jest zwykle wyłączony dla szybkości. W produkcji dodatkowy czas jest uzasadniony znacznym zmniejszeniem rozmiaru pakietu.

Podsumowanie

  • Tree Shaking — automatyczne usuwanie nieużywanych eksportów ES na etapie budowania, zmniejszające rozmiar pakietu o 30–60%
  • Moduły ES — jedyny format obsługujący statyczną analizę; CommonJS nie nadaje się do Tree Shaking
  • Webpack i Rollup zapewniają Tree Shaking przez usedExports i Terser w trybie produkcyjnym
  • Side effects blokują usuwanie modułu; flaga sideEffects: false w package.json rozwiązuje problem dla czystych bibliotek
  • Babel musi być skonfigurowany z modules: false, aby nie przekształcać modułów ES w CommonJS
  • React Native Metro ma ograniczony Tree Shaking, polegając na Terser na etapie minifikacji

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również