Tree Shaking — mecanism de eliminare a codului neutilizat (dead code elimination) în etapa de build a aplicației. Tree Shaking analizează structura statică a modulelor ES și exclude funcțiile, clasele și variabilele exportate care nu sunt importate nicăieri. Conform Webpack Documentation, configurarea corectă a Tree Shaking poate reduce dimensiunea bundle-ului cu 30–60% fără a modifica funcționalitatea aplicației.
Principalele
Tree Shaking (scuturarea copacului) — tehnică de optimizare a codului prin care modulele și funcțiile neutilizate sunt excluse din bundle-ul final. Termenul a fost introdus de echipa Rollup în 2015 și descrie metaforic procesul: copacul dependențelor este scuturat, iar ramurile neutilizate cad. Spre deosebire de optimizarea manuală, Tree Shaking se execută automat în etapa de build.
Tree Shaking funcționează numai cu modulele ES (ECMAScript Modules), unde dependențele sunt definite static prin import și export. CommonJS (require/module.exports) nu suportă Tree Shaking deoarece require se execută dinamic — bundlerul nu poate determina dinainte care funcții sunt utilizate efectiv. Bibliotecile moderne (Lodash, Moment.js, RxJS) lansează versiuni ES pentru suport Tree Shaking.
Rollup — primul bundler care a implementat Tree Shaking în 2015. Spre deosebire de Webpack, Rollup a fost proiectat de la început pentru modulele ES și execută o eliminare mai agresivă a codului mort. Rollup analizează nu numai exporturile individuale, ci și modulele întregi: dacă un modul nu are efecte secundare și niciun export nu este utilizat, Rollup exclude întregul modul din bundle.
Rollup este deosebit de eficient pentru biblioteci și SDK-uri, unde fiecare kilobyte contează. Framework-ul Vue.js folosește Rollup pentru build-ul versiunii de producție. React a trecut la Rollup în 2020. Pentru aplicații se folosește mai des Webpack datorită ecosistemului mai bogat de pluginuri (Hot Module Replacement, code splitting, CSS modules), dar pentru Tree Shaking maxim la build-ul bibliotecilor, Rollup rămâne standardul industriei.
Economia de la Tree Shaking depinde puternic de arhitectura proiectului. Într-o aplicație React cu biblioteca Ant Design, Tree Shaking poate elimina până la 70% din codul componentelor UI. Într-un proiect unde toate importurile sunt specifice și punctuale, economia va fi de 5–15%. Economia medie conform cercetărilor Webpack este de 30–40% din dimensiunea bundle-ului.
| Tip de cod mort | Exemplu | Detectare de Tree Shaking |
|---|---|---|
| Export neutilizat | export function unusedHelper() | Da |
| Import neutilizat | import { unused } from "lib" | Da |
| Ramură moartă de condiție | if (false) { ... } | Nu (eliminat de minificator) |
| Funcție neapelată după DCE | function a(){} a() unde a nu e apelată | Parțial |
Mecanismul Tree Shaking se bazează pe graful de dependențe (dependency graph) pe care bundlerul îl construiește din toate import/export-urile din proiect. În prima etapă, bundlerul parcurge toate fișierele de la punctul de intrare (entry point) și colectează arborele de module. În a doua etapă, se analizează care exporturi din fiecare modul sunt efectiv importate în alte module.
Pentru fiecare modul, Webpack sau Rollup marchează exporturile ca utilizate sau neutilizate. Exporturile neutilizate sunt excluse din bundle. Cu toate acestea, modulul însuși rămâne în bundle dacă cel puțin un export al său este utilizat. Excluderea completă a unui modul este posibilă numai prin flag-ul sideEffects sau dacă modulul nu conține niciun efect secundar.
// utils.js — modul cu funcții
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 — punct de intrare
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// După Tree Shaking — în bundle doar formatDate
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking a exclus formatCurrency și slugify din bundle-ul final, deoarece nu sunt importate în app.js. Dimensiunea modulului utils.js s-a redus de la 3 funcții la 1. Dacă utils.js conține side effects (de exemplu, inițializare globală), Tree Shaking nu va putea elimina nici măcar exporturile neutilizate.
Webpack include suport încorporat pentru Tree Shaking prin pluginul TerserPlugin în modul production. Pentru activarea Tree Shaking sunt suficiente două condiții: mode setat la production (mode: "production") și modulele care folosesc sintaxa ES (import/export). Webpack marchează automat exporturile neutilizate și le transmite lui Terser pentru eliminare.
Configurarea suplimentară usedExports: true în optimization.webpack.config.js activează analiza detaliată a utilizării exporturilor în interiorul modulului. Această opțiune determină care exporturi sunt efectiv utilizate (used) și care sunt doar exportate (provided). Combinația usedExports și Terser oferă eficiență maximă de eliminare a codului mort.
// webpack.config.js — configurare 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 }],
],
},
},
},
],
},
};Parametrul cheie — modules: false în @babel/preset-env. Babel transformă implicit modulele ES în CommonJS, ceea ce distruge Tree Shaking. modules: false interzice Babel să transforme import/export, păstrând sintaxa ES pentru Webpack. concatenateModules combină suplimentar modulele într-un domeniu comun de vizibilitate, reducând numărul de IIFE și dimensiunea bundle-ului.
Side effects (efecte secundare) — acțiunile modulului la importul său, care nu sunt legate de valorile exportate: stiluri globale (import "./styles.css"), polyfill-uri (import "core-js/stable"), inițializarea variabilelor globale sau înregistrarea Service Worker. Dacă un modul conține side effects, bundlerul nu poate să-l elimine în siguranță din bundle, chiar dacă niciun export nu este utilizat.
Flag-ul sideEffects din package.json informează bundlerul care module din pachet nu au efecte secundare. Pentru un pachet unde toate modulele sunt pure (doar export de funcții), trebuie setat "sideEffects": false. Pentru pachete cu CSS sau polyfill-uri — o matrice de căi către fișiere cu efecte secundare: "sideEffects": ["*.css"]. Fără acest flag, Tree Shaking nu va elimina nici măcar funcțiile neutilizate.
Pentru a verifica dacă un modul are efecte secundare, puneți întrebarea: va executa acest import vreo acțiune care nu are legătură cu exportul de valori? import "./styles.css" adaugă CSS în DOM — este un side effect. import { throttle } from "lodash-es" nu are side effects — doar face funcția throttle disponibilă. Polyfill-urile (import "core-js/stable") au side effects — modifică prototipurile globale.
Pentru propriile module se recomandă: mutarea stilurilor și polyfill-urilor în puncte de intrare separate, separarea utilităților pure (funcții fără side effects) de modulele cu efecte secundare (inițializare, logare, înregistrare Service Worker). În package.json al proiectului de nivel superior, setați "sideEffects": false numai dacă toate modulele sunt pure. Dacă există stiluri — setați "sideEffects": ["*.css"] exact.
{
"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"] înseamnă: toate fișierele CSS au efecte secundare (nu pot fi eliminate) și polyfills.js de asemenea. Toate celelalte fișiere JS din pachet sunt pure — pot fi shake-uite în siguranță. Câmpul module indică calea către versiunea ES a pachetului pe care bundlerul trebuie să o folosească în locul versiunii CommonJS (main) pentru Tree Shaking.
React Native cu Metro Bundler suportă o versiune limitată de Tree Shaking. Metro nu efectuează o analiză statică completă a exporturilor utilizate (usedExports) ca Webpack. În schimb, Metro se bazează pe Terser pentru eliminarea părții neutilizate a modulelor în etapa de minificare. Eficiența acestei abordări este mai mică decât Tree Shaking-ul complet din Webpack.
Pentru optimizarea maximă a proiectelor React Native se recomandă: utilizarea bibliotecilor cu module ES (câmpul module în package.json), conectarea pluginurilor babel-plugin-transform-remove-console pentru eliminarea codului de debug și configurarea Metro transformer.minifierConfig pentru Terser. Suplimentar, Ram Bundle (împărțirea bundle-ului în module) reduce încărcarea ecranelor neutilizate.
Întrebări frecvente
CommonJS (require/module.exports) nu suportă analiza statică — require poate fi apelat dinamic în interiorul condițiilor și funcțiilor. Bundlerul nu poate determina care părți ale modulului sunt efectiv utilizate. Numai modulele ES cu import/export static permit Tree Shaking.
TypeScript este complet compatibil cu Tree Shaking cu condiția ca tsconfig.json să fie configurat pentru module ES: "module": "esnext". Compilatorul TypeScript trebuie să păstreze import/export fără transformare în CommonJS. Babel cu @babel/preset-typescript și modules: false transmite corect modulele ES către Webpack.
Webpack Bundle Analyzer — plugin care vizualizează compoziția bundle-ului sub formă de diagramă interactivă. Dacă o bibliotecă este prezentă în bundle, dar funcțiile sale nu sunt utilizate, Tree Shaking nu a funcționat. De asemenea, puteți analiza fișierul de ieșire: găsiți exportul neutilizat în textul bundle-ului prin grep.
Lodash v4 este distribuit ca pachet CommonJS. Pentru Tree Shaking trebuie să folosiți lodash-es — versiunea ES a bibliotecii. Înlocuiți import throttle from "lodash/throttle" cu import { throttle } from "lodash-es" și configurați resolve.alias în Webpack pentru înlocuirea lodash cu lodash-es.
Tree Shaking crește ușor timpul de build (cu 5–15%), deoarece adaugă etapa de analiză a grafului de dependențe și marcarea exporturilor utilizate. În modul development, Tree Shaking este de obicei dezactivat pentru viteză. În production, timpul suplimentar este justificat de reducerea semnificativă a dimensiunii bundle-ului.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și