Tree Shaking — a nem használt kód eltávolításának mechanizmusa (dead code elimination) az alkalmazás buildelési szakaszában. A Tree Shaking elemzi az ES-modulok statikus szerkezetét és kizárja az exportált függvényeket, osztályokat és változókat, amelyek sehol sincsenek importálva. A Webpack Documentation szerint a Tree Shaking megfelelő konfigurációja 30–60%-kal csökkentheti a bundle méretét anélkül, hogy megváltoztatná az alkalmazás funkcióit.
Főbb pontok
Tree Shaking (fa megrázása) — kódoptimalizálási technika, amely során a nem használt modulok és függvények kikerülnek a végső bundle-ből. A kifejezést a Rollup csapat vezette be 2015-ben, és metaforikusan írja le a folyamatot: a függőségi fát megrázzák, és a nem használt ágak lehullanak. A kézi optimalizálással ellentétben a Tree Shaking automatikusan történik a buildelési szakaszban.
A Tree Shaking csak ES-modulokkal (ECMAScript Modules) működik, ahol a függőségek statikusan kerülnek meghatározásra import és export útján. A CommonJS (require/module.exports) nem támogatja a Tree Shakinget, mert a require dinamikusan fut — a buildelő nem tudja előre meghatározni, mely függvényeket használják ténylegesen. A modern könyvtárak (Lodash, Moment.js, RxJS) ES-verziókat adnak ki a Tree Shaking támogatásához.
Rollup — az első buildelő, amely 2015-ben bevezette a Tree Shakinget. A Webpackkal ellentétben a Rollupot kezdettől fogva ES-modulokra tervezték, és agresszívebb holt kód eltávolítást végez. A Rollup nemcsak az egyes exportokat elemzi, hanem a teljes modulokat is: ha egy modulnak nincs mellékhatása és egyetlen export sem használt, a Rollup az egész modult kizárja a bundle-ből.
A Rollup különösen hatékony könyvtárak és SDK-k esetében, ahol minden kilobájt számít. A Vue.js keretrendszer a Rollupot használja a production verzió buildeléséhez. A React 2020-ban váltott Rollupra. Alkalmazásokhoz gyakrabban használják a Webpackot a gazdagabb plugin-ökoszisztéma miatt (Hot Module Replacement, code splitting, CSS modules), de a maximális Tree Shakinghez könyvtárak buildelésekor a Rollup marad az ipari szabvány.
A megtakarítás a Tree Shakingtől erősen függ a projekt architektúrájától. Egy React alkalmazásban az Ant Design könyvtárral a Tree Shaking akár 70%-kal is csökkentheti az UI komponensek kódját. Egy olyan projektben, ahol minden import specifikus és pontos, a megtakarítás 5–15% lesz. Az átlagos megtakarítás a Webpack kutatásai szerint a bundle méretének 30–40%-a.
| Holt kód típusa | Példa | Felderítés Tree Shakingkel |
|---|---|---|
| Nem használt export | export function unusedHelper() | Igen |
| Nem használt import | import { unused } from "lib" | Igen |
| Feltétel holt ága | if (false) { ... } | Nem (a minifikátor eltávolítja) |
| Nem hívott függvény DCE után | function a(){} a() ahol a nincs meghívva | Részben |
A Tree Shaking mechanizmusa a függőségi gráfon (dependency graph) alapul, amelyet a buildelő a projekt összes import/exportjából épít fel. Az első szakaszban a buildelő végigjárja az összes fájlt a belépési ponttól (entry point) és összegyűjti a modulok fáját. A második szakaszban elemzi, hogy mely exportok az egyes modulokból ténylegesen importálva vannak más modulokban.
Minden modul esetében a Webpack vagy Rollup az exportokat használtként vagy nem használtként jelöli meg. A nem használt exportok kikerülnek a bundle-ből. Maga a modul azonban a bundle-ben marad, ha legalább egy exportja használatban van. Egy modul teljes kizárása csak a sideEffects jelzőn keresztül lehetséges, vagy ha a modul nem tartalmaz mellékhatásokat.
// utils.js — modul függvényekkel
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 — belépési pont
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// Tree Shaking után — a bundle-ben csak formatDate
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking kizárta a formatCurrency és slugify függvényeket a végső bundle-ből, mert nem importálják őket az app.js-ben. Az utils.js modul mérete 3 függvényről 1-re csökkent. Ha az utils.js side effects-et tartalmaz (pl. globális inicializálás), a Tree Shaking még a nem használt exportokat sem tudja eltávolítani.
Webpack beépített támogatást tartalmaz a Tree Shakinghez a TerserPlugin-on keresztül production módban. A Tree Shaking bekapcsolásához két feltétel elegendő: a mode productionre van állítva (mode: "production") és a modulok ES szintaxist használnak (import/export). A Webpack automatikusan megjelöli a nem használt exportokat és továbbítja őket a Terser-nek eltávolításra.
További konfiguráció usedExports: true az optimization.webpack.config.js-ben bekapcsolja az exportok használatának részletes elemzését a modulon belül. Ez az opció meghatározza, hogy mely exportok ténylegesen használtak (used), és melyek csak exportáltak (provided). A usedExports és Terser kombinációja maximális hatékonyságot nyújt a holt kód eltávolításában.
// webpack.config.js — Tree Shaking konfiguráció
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 }],
],
},
},
},
],
},
};A kulcs paraméter — modules: false a @babel/preset-env-ben. A Babel alapértelmezés szerint ES-modulokat CommonJS-re alakítja, ami megöli a Tree Shakinget. A modules: false megtiltja a Babel-nek, hogy átalakítsa az import/export-ot, megtartva az ES szintaxist a Webpack számára. A concatenateModules emellett egyesíti a modulokat egy közös láthatósági tartományba, csökkentve az IIFE-k számát és a bundle méretét.
Side effects (mellékhatások) — a modul műveletei az importáláskor, amelyek nem kapcsolódnak az exportált értékekhez: globális stílusok (import "./styles.css"), polyfill-ek (import "core-js/stable"), globális változók inicializálása vagy Service Worker regisztráció. Ha egy modul mellékhatásokat tartalmaz, a buildelő nem távolíthatja el biztonságosan a bundle-ből, még akkor sem, ha egyetlen export sincs használatban.
A sideEffects jelző a package.json-ben tájékoztatja a buildelőt arról, hogy a csomag mely moduljainak nincsenek mellékhatásai. Egy olyan csomag esetében, ahol minden modul tiszta (csak függvények exportálása), "sideEffects": false értéket kell beállítani. CSS-t vagy polyfill-eket tartalmazó csomagok esetén — a mellékhatásokkal rendelkező fájlok elérési útjainak tömbje: "sideEffects": ["*.css"]. E jelző nélkül a Tree Shaking még a nem használt függvényeket sem távolítja el.
Annak ellenőrzéséhez, hogy egy modulnak vannak-e mellékhatásai, tegye fel a kérdést: végrehajt-e ez az import bármilyen műveletet, amely nem kapcsolódik az értékek exportálásához? import "./styles.css" CSS-t ad a DOM-hoz — ez egy side effect. import { throttle } from "lodash-es" nem rendelkezik side effects-szel — csak elérhetővé teszi a throttle függvényt. A polyfill-ek (import "core-js/stable") side effects-szel rendelkeznek — módosítják a globális prototípusokat.
Saját modulokhoz ajánlott: a stílusok és polyfill-ek külön belépési pontokba helyezése, a tiszta segédprogramok (mellékhatások nélküli függvények) elkülönítése a mellékhatásokkal rendelkező moduloktól (inicializálás, naplózás, Service Worker regisztráció). A felső szintű projekt package.json-jában "sideEffects": false csak akkor állítson be, ha minden modul tiszta. Ha vannak stílusok — "sideEffects": ["*.css"] pontosan állítson be.
{
"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"] azt jelenti: az összes CSS fájlnak vannak mellékhatásai (nem távolíthatók el), és a polyfills.js-nek is. A csomag összes többi JS fájlja tiszta — biztonságosan shake-elhetők. A module mező a csomag ES-verziójának elérési útját jelzi, amelyet a buildelőnek használnia kell a CommonJS verzió (main) helyett a Tree Shakinghez.
React Native a Metro Bundlerrel korlátozott Tree Shaking verziót támogat. A Metro nem végez teljes statikus elemzést a használt exportokról (usedExports) mint a Webpack. Ehelyett a Metro a Terser-re támaszkodik a modulok nem használt részének eltávolításához a minifikációs szakaszban. Ennek a megközelítésnek a hatékonysága alacsonyabb, mint a teljes Tree Shaking a Webpackban.
A maximális optimalizáláshoz React Native projektekben ajánlott: ES-modulokkal rendelkező könyvtárak használata (module mező a package.json-ben), babel-plugin-transform-remove-console pluginek csatlakoztatása a hibakereső kód eltávolításához, és a Metro transformer.minifierConfig konfigurálása a Terser számára. Ezenkívül a Ram Bundle (a bundle modulokra bontása) csökkenti a nem használt képernyők betöltését.
Gyakran Ismételt Kérdések
CommonJS (require/module.exports) nem támogatja a statikus elemzést — a require dinamikusan hívható feltételek és függvények belsejében. A buildelő nem tudja meghatározni, hogy a modul mely részei használatosak ténylegesen. Csak az ES-modulok statikus import/export-tal teszik lehetővé a Tree Shakinget.
TypeScript teljes mértékben kompatibilis a Tree Shakinggel, feltéve hogy a tsconfig.json ES-modulokra van konfigurálva: "module": "esnext". A TypeScript fordítónak meg kell őriznie az import/export-ot CommonJS-re való átalakítás nélkül. A Babel @babel/preset-typescript-tel és modules: false-szal szintén helyesen továbbítja az ES-modulokat a Webpacknak.
Webpack Bundle Analyzer — egy plugin, amely interaktív diagram formájában vizualizálja a bundle összetételét. Ha egy könyvtár jelen van a bundle-ben, de függvényei nincsenek használatban, a Tree Shaking nem működött. A kimeneti fájl is elemezhető: keresse meg a nem használt exportot a bundle szövegében grep segítségével.
Lodash v4 CommonJS csomagként kerül terjesztésre. A Tree Shakinghez használja a lodash-es-t — a könyvtár ES-verzióját. Cserélje ki az import throttle from "lodash/throttle"-t import { throttle } from "lodash-es"-re, és konfigurálja a resolve.alias-t a Webpackban a lodash lodash-es-re cseréléséhez.
Tree Shaking enyhén növeli a build időt (5–15%-kal), mert hozzáadja a függőségi gráf elemzésének és a használt exportok megjelölésének szakaszát. Fejlesztési módban a Tree Shaking általában ki van kapcsolva a sebesség érdekében. Production-ben a többletidőt a bundle méretének jelentős csökkenése indokolja.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is