Tree Shaking: τι είναι, μηχανισμός αφαίρεσης νεκρού κώδικα και εργαλεία

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-05-18 Χρόνος ανάγνωσης: 8 λεπ

Tree Shaking — μηχανισμός αφαίρεσης αχρησιμοποίητου κώδικα (dead code elimination) στο στάδιο κατασκευής της εφαρμογής. Το Tree Shaking αναλύει τη στατική δομή των ES-modules και εξαιρεί εξαγόμενες συναρτήσεις, κλάσεις και μεταβλητές που δεν εισάγονται πουθενά. Σύμφωνα με το Webpack Documentation, η σωστή ρύθμιση του Tree Shaking μπορεί να μειώσει το μέγεθος του bundle κατά 30–60% χωρίς να αλλάξει τη λειτουργικότητα της εφαρμογής.

Κύρια Σημεία

  • Tree Shaking — αφαίρεση αχρησιμοποίητων εξαγωγών από ES-modules βάσει στατικής ανάλυσης import/export
  • ES-modules (import/export) — η μόνη μορφή που υποστηρίζει Tree Shaking; το CommonJS δεν υποστηρίζεται
  • Webpack και Rollup — οι κύριοι κατασκευαστές με υποστήριξη Tree Shaking μέσω plugins
  • Side effects — παρενέργειες σε modules μπλοκάρουν το Tree Shaking; η σημαία sideEffects: false στο package.json λύνει το πρόβλημα
  • Used exports — ανάλυση χρήσης εξαγωγών σε production mode του Webpack για ακριβή αφαίρεση νεκρού κώδικα

Τι είναι το Tree Shaking;

Tree Shaking (τίναγμα δέντρου) — τεχνική βελτιστοποίησης κώδικα κατά την οποία modules και συναρτήσεις που δεν χρησιμοποιούνται στην εφαρμογή εξαιρούνται από το τελικό bundle. Ο όρος εισήχθη από την ομάδα Rollup το 2015 και περιγράφει μεταφορικά τη διαδικασία: το δέντρο εξαρτήσεων τινάζεται και τα αχρησιμοποίητα κλαδιά πέφτουν. Σε αντίθεση με τη χειροκίνητη βελτιστοποίηση, το Tree Shaking εκτελείται αυτόματα στο στάδιο κατασκευής.

Το Tree Shaking λειτουργεί μόνο με ES-modules (ECMAScript Modules), όπου οι εξαρτήσεις καθορίζονται στατικά μέσω import και export. Το CommonJS (require/module.exports) δεν υποστηρίζει Tree Shaking επειδή το require εκτελείται δυναμικά — ο κατασκευαστής δεν μπορεί να καθορίσει εκ των προτέρων ποιες συναρτήσεις χρησιμοποιούνται πραγματικά. Οι σύγχρονες βιβλιοθήκες (Lodash, Moment.js, RxJS) κυκλοφορούν ES εκδόσεις για υποστήριξη Tree Shaking.

Rollup: πρωτοπόρος του Tree Shaking

Rollup — ο πρώτος κατασκευαστής που εφάρμοσε το Tree Shaking το 2015. Σε αντίθεση με το Webpack, το Rollup σχεδιάστηκε εξαρχής για ES-modules και εκτελεί πιο επιθετική αφαίρεση νεκρού κώδικα. Το Rollup αναλύει όχι μόνο μεμονωμένες εξαγωγές, αλλά ολόκληρα modules: εάν ένα module δεν έχει παρενέργειες και καμία εξαγωγή δεν χρησιμοποιείται, το Rollup εξαιρεί ολόκληρο το module από το bundle.

Το Rollup είναι ιδιαίτερα αποτελεσματικό για βιβλιοθήκες και SDK, όπου κάθε kilobyte έχει σημασία. Το πλαίσιο Vue.js χρησιμοποιεί Rollup για την κατασκευή της production έκδοσης. Το React μεταπήδησε στο Rollup το 2020. Για εφαρμογές χρησιμοποιείται συχνότερα το Webpack λόγω του πλουσιότερου οικοσυστήματος plugins (Hot Module Replacement, code splitting, CSS modules), αλλά για μέγιστο Tree Shaking κατά την κατασκευή βιβλιοθηκών, το Rollup παραμένει το βιομηχανικό πρότυπο.

Η εξοικονόμηση από το Tree Shaking εξαρτάται σε μεγάλο βαθμό από την αρχιτεκτονική του έργου. Σε μια εφαρμογή React με τη βιβλιοθήκη Ant Design, το Tree Shaking μπορεί να αφαιρέσει έως και 70% του κώδικα UI components. Σε ένα έργο όπου όλες οι εισαγωγές είναι συγκεκριμένες και στοχευμένες, η εξοικονόμηση θα είναι 5–15%. Η μέση εξοικονόμηση σύμφωνα με έρευνες του Webpack είναι 30–40% του μεγέθους του bundle.

Τι είναι ο νεκρός κώδικας

Τύπος νεκρού κώδικαΠαράδειγμαΑνίχνευση από Tree Shaking
Αχρησιμοποίητη εξαγωγήexport function unusedHelper()Ναι
Αχρησιμοποίητη εισαγωγήimport { unused } from "lib"Ναι
Νεκρός κλάδος συνθήκηςif (false) { ... }Όχι (αφαιρείται από minifier)
Μη κληθείσα συνάρτηση μετά DCEfunction a(){} a() όπου η a δεν καλείταιΜερικώς

Πώς λειτουργεί το Tree Shaking: στατική ανάλυση modules

Ο μηχανισμός Tree Shaking βασίζεται στο γράφημα εξαρτήσεων (dependency graph) που ο κατασκευαστής χτίζει από όλα τα import/export στο έργο. Στο πρώτο στάδιο, ο κατασκευαστής διατρέχει όλα τα αρχεία από το σημείο εισόδου (entry point) και συλλέγει το δέντρο modules. Στο δεύτερο στάδιο, αναλύεται ποιες εξαγωγές από κάθε module εισάγονται πραγματικά σε άλλα modules.

Για κάθε module, το Webpack ή Rollup σηματοδοτεί τις εξαγωγές ως χρησιμοποιούμενες ή αχρησιμοποίητες. Οι αχρησιμοποίητες εξαγωγές εξαιρούνται από το bundle. Ωστόσο, το ίδιο το module παραμένει στο bundle εάν τουλάχιστον μία εξαγωγή του χρησιμοποιείται. Η πλήρης εξαίρεση ενός module είναι δυνατή μόνο μέσω της σημαίας sideEffects ή εάν το module δεν περιέχει παρενέργειες.

Παράδειγμα: πριν και μετά το Tree Shaking

js
// utils.js — module με συναρτήσεις
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 — σημείο εισόδου
import { formatDate } from "./utils";

const today = formatDate(new Date());
console.log(today);
js
// Μετά το Tree Shaking — στο bundle μόνο formatDate
function formatDate(date) {
  return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);

Tree Shaking εξαίρεσε τα formatCurrency και slugify από το τελικό bundle, επειδή δεν εισάγονται στο app.js. Το μέγεθος του module utils.js μειώθηκε από 3 συναρτήσεις σε 1. Εάν το utils.js περιέχει side effects (π.χ. καθολική αρχικοποίηση), το Tree Shaking δεν θα μπορέσει να αφαιρέσει ούτε τις αχρησιμοποίητες εξαγωγές.

Tree Shaking στο Webpack: ρύθμιση και βελτιστοποίηση

Webpack περιλαμβάνει ενσωματωμένη υποστήριξη για Tree Shaking μέσω του TerserPlugin σε production mode. Για την ενεργοποίηση του Tree Shaking αρκούν δύο προϋποθέσεις: το mode έχει οριστεί σε production (mode: "production") και τα modules χρησιμοποιούν ES σύνταξη (import/export). Το Webpack σηματοδοτεί αυτόματα τις αχρησιμοποίητες εξαγωγές και τις μεταβιβάζει στο Terser για αφαίρεση.

Πρόσθετη ρύθμιση usedExports: true στο optimization.webpack.config.js ενεργοποιεί λεπτομερή ανάλυση χρήσης εξαγωγών εντός του module. Αυτή η επιλογή καθορίζει ποιες εξαγωγές χρησιμοποιούνται πραγματικά (used) και ποιες είναι μόνο εξαγόμενες (provided). Ο συνδυασμός usedExports και Terser δίνει μέγιστη αποτελεσματικότητα αφαίρεσης νεκρού κώδικα.

Ρύθμιση Webpack για Tree Shaking

js
// webpack.config.js — ρύθμιση 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 }],
            ],
          },
        },
      },
    ],
  },
};

Η βασική παράμετρος — modules: false στο @babel/preset-env. Το Babel από προεπιλογή μετατρέπει ES-modules σε CommonJS, το οποίο σκοτώνει το Tree Shaking. Το modules: false απαγορεύει στο Babel να μετατρέπει import/export, διατηρώντας την ES σύνταξη για το Webpack. Το concatenateModules επιπλέον συνδυάζει modules σε ένα κοινό πεδίο ορατότητας, μειώνοντας τον αριθμό IIFE και το μέγεθος του bundle.

Το πρόβλημα των side effects και η σημαία sideEffects

Side effects (παρενέργειες) — ενέργειες του module κατά την εισαγωγή του που δεν σχετίζονται με εξαγόμενες τιμές: καθολικά στυλ (import "./styles.css"), polyfill (import "core-js/stable"), αρχικοποίηση καθολικών μεταβλητών ή εγγραφή Service Worker. Εάν ένα module περιέχει side effects, ο κατασκευαστής δεν μπορεί να το αφαιρέσει με ασφάλεια από το bundle, ακόμη και αν καμία εξαγωγή δεν χρησιμοποιείται.

Η σημαία sideEffects στο package.json ενημερώνει τον κατασκευαστή ποια modules στο πακέτο δεν έχουν παρενέργειες. Για ένα πακέτο όπου όλα τα modules είναι καθαρά (μόνο εξαγωγή συναρτήσεων), πρέπει να οριστεί "sideEffects": false. Για πακέτα με CSS ή polyfill — πίνακας διαδρομών προς αρχεία με παρενέργειες: "sideEffects": ["*.css"]. Χωρίς αυτή τη σημαία, το Tree Shaking δεν θα αφαιρέσει ούτε τις αχρησιμοποίητες συναρτήσεις.

Πώς να εντοπίσετε side effects στον δικό σας κώδικα

Για να ελέγξετε αν ένα module έχει παρενέργειες, κάντε την ερώτηση: θα εκτελέσει αυτή η εισαγωγή οποιαδήποτε ενέργεια που δεν σχετίζεται με την εξαγωγή τιμών; import "./styles.css" προσθέτει CSS στο DOM — αυτό είναι side effect. import { throttle } from "lodash-es" δεν έχει side effects — απλώς καθιστά τη συνάρτηση throttle διαθέσιμη. Τα polyfill (import "core-js/stable") έχουν side effects — τροποποιούν καθολικά πρωτότυπα.

Για δικά σας modules συνιστάται: μεταφορά στυλ και polyfill σε ξεχωριστά σημεία εισόδου, διαχωρισμός καθαρών βοηθητικών προγραμμάτων (συναρτήσεις χωρίς side effects) από modules με παρενέργειες (αρχικοποίηση, καταγραφή, εγγραφή Service Worker). Στο package.json του ανώτερου επιπέδου έργου, ορίστε "sideEffects": false μόνο εάν όλα τα modules είναι καθαρά. Εάν υπάρχουν στυλ — ορίστε "sideEffects": ["*.css"] με ακρίβεια.

Παράδειγμα ρύθμισης 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"] σημαίνει: όλα τα αρχεία CSS έχουν παρενέργειες (δεν μπορούν να αφαιρεθούν) και το polyfills.js επίσης. Όλα τα άλλα αρχεία JS στο πακέτο είναι καθαρά — μπορούν να shake-αριστούν με ασφάλεια. Το πεδίο module υποδεικνύει τη διαδρομή προς την ES έκδοση του πακέτου που πρέπει να χρησιμοποιήσει ο κατασκευαστής αντί της CommonJS έκδοσης (main) για Tree Shaking.

Tree Shaking σε React Native και Metro

React Native με Metro Bundler υποστηρίζει περιορισμένη έκδοση Tree Shaking. Το Metro δεν εκτελεί πλήρη στατική ανάλυση χρησιμοποιούμενων εξαγωγών (usedExports) όπως το Webpack. Αντίθετα, το Metro βασίζεται στο Terser για την αφαίρεση του αχρησιμοποίητου μέρους των modules στο στάδιο minification. Η αποτελεσματικότητα αυτής της προσέγγισης είναι χαμηλότερη από το πλήρες Tree Shaking στο Webpack.

Για μέγιστη βελτιστοποίηση έργων React Native συνιστάται: χρήση βιβλιοθηκών με ES-modules (πεδίο module στο package.json), σύνδεση plugins babel-plugin-transform-remove-console για αφαίρεση κώδικα αποσφαλμάτωσης και ρύθμιση Metro transformer.minifierConfig για Terser. Επιπλέον, το Ram Bundle (διαίρεση του bundle σε modules) μειώνει τη φόρτωση αχρησιμοποίητων οθονών.

Συχνές Ερωτήσεις

Γιατί το Tree Shaking δεν λειτουργεί με CommonJS;

CommonJS (require/module.exports) δεν υποστηρίζει στατική ανάλυση — το require μπορεί να κληθεί δυναμικά εντός συνθηκών και συναρτήσεων. Ο κατασκευαστής δεν μπορεί να καθορίσει ποια μέρη του module χρησιμοποιούνται πραγματικά. Μόνο ES-modules με στατικά import/export επιτρέπουν Tree Shaking.

Μπορεί να χρησιμοποιηθεί το Tree Shaking με TypeScript;

TypeScript είναι πλήρως συμβατό με Tree Shaking υπό την προϋπόθεση ότι το tsconfig.json έχει ρυθμιστεί για ES-modules: "module": "esnext". Ο μεταγλωττιστής TypeScript πρέπει να διατηρεί import/export χωρίς μετατροπή σε CommonJS. Το Babel με @babel/preset-typescript και modules: false μεταβιβάζει επίσης σωστά ES-modules στο Webpack.

Πώς να ελέγξω αν το Tree Shaking λειτούργησε;

Webpack Bundle Analyzer — plugin που οπτικοποιεί τη σύνθεση του bundle ως διαδραστικό διάγραμμα. Εάν μια βιβλιοθήκη υπάρχει στο bundle αλλά οι συναρτήσεις της δεν χρησιμοποιούνται, το Tree Shaking δεν λειτούργησε. Επίσης, μπορείτε να αναλύσετε το αρχείο εξόδου: βρείτε την αχρησιμοποίητη εξαγωγή στο κείμενο του bundle μέσω grep.

Γιατί το Lodash δεν κάνει tree-shake από προεπιλογή;

Lodash v4 διανέμεται ως CommonJS πακέτο. Για Tree Shaking πρέπει να χρησιμοποιήσετε lodash-es — την ES έκδοση της βιβλιοθήκης. Αντικαταστήστε το import throttle from "lodash/throttle" με import { throttle } from "lodash-es" και ρυθμίστε το resolve.alias στο Webpack για αντικατάσταση του lodash με lodash-es.

Επηρεάζει το Tree Shaking τον χρόνο κατασκευής;

Tree Shaking αυξάνει ελαφρώς τον χρόνο κατασκευής (κατά 5–15%), καθώς προσθέτει στάδιο ανάλυσης γραφήματος εξαρτήσεων και σήμανσης χρησιμοποιούμενων εξαγωγών. Σε development mode, το Tree Shaking είναι συνήθως απενεργοποιημένο για ταχύτητα. Σε production, ο πρόσθετος χρόνος δικαιολογείται από τη σημαντική μείωση του μεγέθους του bundle.

Σύνοψη

  • Tree Shaking — αυτόματη αφαίρεση αχρησιμοποίητων ES εξαγωγών στο στάδιο κατασκευής, μείωση μεγέθους bundle κατά 30–60%
  • ES-modules — η μόνη μορφή που υποστηρίζει στατική ανάλυση; το CommonJS δεν είναι κατάλληλο για Tree Shaking
  • Webpack και Rollup παρέχουν Tree Shaking μέσω usedExports και Terser σε production mode
  • Side effects μπλοκάρουν την αφαίρεση module; η σημαία sideEffects: false στο package.json λύνει το πρόβλημα για καθαρές βιβλιοθήκες
  • Babel πρέπει να ρυθμιστεί με modules: false για να μην μετατρέπει ES-modules σε CommonJS
  • React Native Metro έχει περιορισμένο Tree Shaking, βασιζόμενο στο Terser στο στάδιο minification

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης