JavaScript bundle optimalizálás 2026: tree shaking, code splitting és dinamikus import
Hogyan csökkentsd a JavaScript bundle méretét 2026-ban: tree shaking, code splitting és dinamikus import a gyakorlatban, konkrét példákkal és mérési adatokkal.
A JavaScript bundle optimalizálás 2026-ban három pillérre épül: a tree shaking eltávolítja a holt kódot a függőségi gráfból, a code splitting útvonal- és komponens-szinten darabolja a kötegeket, a dinamikus import() pedig csak akkor tölti be a kódot, amikor a felhasználónak ténylegesen szüksége van rá. Egy tipikus React vagy Vue alkalmazás kezdeti bundle-je 2 MB-ról 200–400 KB-ra csökkenthető, ha ezt a hármast helyesen alkalmazzuk, és ezzel együtt az LCP és INP mutatók is mérhetően javulnak.
Őszintén szólva, a legtöbb projektnél ahol méréseket végeztem, a bundle nagyobbik fele „véletlenül" került oda. Egyetlen rossz import, egy elfelejtett konfig sor, és máris ott van a teljes moment.js a kezdeti payload-ban. Erről szól ez a cikk.
A tree shaking csak ES modulokkal (ESM) működik megbízhatóan; minden CommonJS require() hívás megöli a statikus elemzést.
A sideEffects: false mező a package.json-ban az egyik legnagyobb hatású beállítás. Gyakran 30–40%-kal csökkenti a végső bundle méretét.
Az útvonal-szintű (route-based) code splitting a legmagasabb megtérülésű első lépés: minden modern keretrendszer támogatja, és azonnal harmadolja a kezdeti payload-ot.
A dinamikus import() Promise-t ad vissza, és webpack, Vite, Rollup, esbuild, valamint Turbopack is automatikusan külön chunk-ot készít belőle.
A Brotli tömörítés a Gzip-hez képest átlagosan 17–20%-kal kisebb átviteli méretet ad, és minden CDN-en be kell kapcsolni.
Az 50 KB-os performance budget chunk-onként reális 2026-ban; ezen felül Lighthouse CI-vel automatikusan blokkolni kell a regressziót.
Mi az a tree shaking és hogyan működik?
A tree shaking egy statikus elemzésen alapuló technika, amely a bundler dependency graph-ját végigjárva azonosítja és kivágja azokat az export-okat, amelyeket sehol nem hívnak meg az alkalmazásban. A „fa rázása" metafora arra utal, hogy a használt kód a törzs és az ágak, az érintetlen kód pedig a száraz levél, amit lerázunk. A módszer kizárólag ES modulokkal működik megbízhatóan, mert az import és export deklarációk értéke fordítási időben ismert. A CommonJS require() ezzel szemben futásidejű függvényhívás, amit a bundler nem tud megbízhatóan elemezni.
A gyakorlatban három feltétel együttes teljesülése kell ahhoz, hogy működjön. Először, a forrás- és a függőségi kódnak ESM formátumúnak kell lennie. Ezt a package.json-ban a "type": "module" vagy a "exports" mező "import" kulcsa jelzi. Másodszor, a könyvtárakat nevesített importtal kell behúzni (import { debounce } from 'lodash-es'), nem pedig namespace importtal (import * as _ from 'lodash-es'), mert utóbbi az egész modult megőrzi. Harmadszor, a build módnak production-nek kell lennie, hogy a minifier (Terser, esbuild minify) ténylegesen kivágja a megjelölt kódot.
Egy tipikus mérési adat: egy 47 KB-os UI könyvtárból, amelyből csak két komponenst használ az alkalmazás, helyes tree shaking-gel 6–8 KB marad. Helytelen konfigurációval (például "sideEffects": true) a teljes 47 KB bekerül a végső bundle-be. Pontosan ebbe a hibába futottam bele tavaly egy ügyfél projektjén: a fejlesztők hetekig kerestek 60 KB „ismeretlen" kódot, ami egyetlen rossz package.json mező miatt nem rázódott le.
A code splitting a monolitikus bundle-t több kisebb chunk-ra bontja, amelyeket a böngésző csak akkor tölt le, amikor szüksége van rájuk. Három fő stratégia létezik 2026-ban, és a legtöbb projekt mindhármat egyszerre alkalmazza.
Az útvonal-szintű splitting minden route-hoz külön chunk-ot generál. Egy 12 oldalas alkalmazásnál ez azt jelenti, hogy a kezdeti betöltés csak az adott URL kódját kéri le, nem mindet. A komponens-szintű splitting a nehéz, ritkán használt UI elemeket (modal-ok, rich text editor, charting könyvtár) külön chunk-ba szervezi. A vendor splitting pedig a stabil, ritkán változó harmadik feles függőségeket egy hosszabb cache-elhető chunk-ba teszi, hogy a saját kódunk változása ne érvénytelenítse a böngésző cache-ét.
React esetében az útvonal-szintű splitting a React.lazy és Suspense kombinációjával történik. Egy konkrét példa:
Vue 3-ban a defineAsyncComponent, Angular-ban a loadChildren, SvelteKit-ben pedig a fájl-alapú routing automatikusan biztosítja ugyanezt. A harmadik fél által beágyazott szkriptek külön optimalizálási problémakör, erről részletesen a harmadik fél szkriptek optimalizálása cikkben írtam.
Dinamikus import() a gyakorlatban
A dinamikus import() egy beépített JavaScript függvényhívás, amely Promise-t ad vissza a betöltött modullal. Ez az alap, amire minden modern code splitting épül: a bundler automatikusan külön chunk-ot generál minden import() kifejezéshez, és a hash-elt fájlnevet beleírja a runtime manifest-be. A funkció támogatva van minden 2018 utáni böngészőben, és a build idejű bundler-ek (Webpack 5+, Vite 5+, Rollup 4+, esbuild 0.20+, Turbopack) is natív szinten kezelik.
A leggyakoribb felhasználási minta nehéz UI feature betöltése interakcióra. Például egy chart könyvtár (50–150 KB), amit csak akkor van értelme letölteni, ha a felhasználó tényleg megnyit egy „Statisztikák" panelt:
// A gomb kattintásakor töltjük be a chart kódot, nem a kezdeti betöltéssel.
button.addEventListener('click', async () => {
const { renderChart } = await import('./chart-module.js');
renderChart(document.getElementById('chart'), data);
});
Egy fejlettebb minta a feltételes betöltés: ha a felhasználó eszköze nem támogatja a IntersectionObserver-t, betöltünk egy polyfillt; ellenkező esetben semmit nem tölt le. Az MDN dinamikus import dokumentáció részletesen kifejti a szintaktikai szabályokat és a Promise-alapú hibakezelést.
A 2026-os bundler piacon öt fő szereplő dominál, mindegyik más erősséggel. A Webpack még mindig a legkonfigurálhatóbb és a legtöbb plugin-nel rendelkezik, de a build sebesség jelentősen elmarad a fiatalabb riválisaitól. A Vite (Rollup-alapú production build-del, esbuild-alapú dev szerverrel) a React, Vue és Svelte ökoszisztémák de facto választása lett. A Rollup tisztább tree shaking eredményt ad, és könyvtár-publikálásra ideális. Az esbuild a leggyorsabb (Go-ban írva), és kis-közepes projektekhez kiváló. A Turbopack a Next.js 15+ alapértelmezett bundlere; Rust-ban íródott, és cache-alapú inkrementális build-ekkel a Webpack 10–20-szorosát produkálja.
Tulajdonság
Webpack 5
Vite 6
Rollup 4
esbuild
Turbopack
Nyelv
JavaScript
JS + Go (esbuild)
JavaScript
Go
Rust
Dev build (közepes projekt)
15–25 s
200–500 ms
5–10 s
50–200 ms
100–400 ms
Tree shaking minőség
Jó
Kiváló (Rollup)
Kiváló
Közepes
Jó
HMR sebesség
Lassú
Azonnali (ESM)
n.a.
Gyors
Azonnali
Plugin ökoszisztéma
Hatalmas
Nagy (Rollup-kompatibilis)
Nagy
Kicsi
Növekvő
Production output méret
Optimális
Optimális
Legoptimálisabb
Jó
Optimális
Tanulási görbe
Meredek
Lapos
Közepes
Lapos
Lapos
A választás során a legfontosabb szempont nem a build sebesség, hanem a tree shaking minősége és a production output mérete. Egy gyors build, amely 30%-kal nagyobb bundle-t ad, hosszú távon rosszabb felhasználói élményt eredményez.
A sideEffects mező és a holt kód eltávolítása
A sideEffects mező a package.json-ban az egyik legnagyobb, de leggyakrabban elfelejtett tree shaking optimalizáció. Alapértelmezetten a bundler azt feltételezi, hogy minden modul végrehajthat mellékhatásokat (CSS import, globális prototípus-bővítés, polyfill regisztráció), és emiatt nem meri kidobni — még akkor sem, ha egyetlen exportját sem használjuk. A "sideEffects": false deklaráció explicit ígéret a bundler felé, hogy a csomag tisztán importálható.
A profilozó munkámban ez az egyetlen sor 30–40%-os bundle méret csökkenést hozott olyan alkalmazásoknál, amelyek korábban moment.js-t vagy lodash (nem ES) variánsokat húztak be. Egyszerűen azért, mert a sideEffects: true miatt minden használatlan locale és segédfüggvény bekerült. A Webpack hivatalos tree shaking guide-ja ezt a mezőt központi elemnek nevezi.
Bundle elemzés és a legnagyobb fogyasztók azonosítása
Optimalizálni csak azt érdemes, amit mérünk. Ezért minden bundle optimalizálási projekt első lépése egy bundle analizátor futtatása. Webpack-hez a webpack-bundle-analyzer, Vite és Rollup esetén a rollup-plugin-visualizer, esbuild-hez az esbuild-visualizer, Turbopack-hez pedig a Next.js beépített @next/bundle-analyzer a választás.
A treemap nézet azonnal megmutatja, mely chunk-ok foglalják a legtöbb helyet. A tapasztalatom szerint a top 3 függőség mindig az alkalmazás kód 50–70%-át teszi ki. Tipikus „bűnösök": régi moment.js (67 KB), teljes lodash import (24 KB), chart.js minden adapterrel (180 KB), polyfill libraryk modern target-tel (15–40 KB feleslegesen). A trace ezeknél a chunk-oknál mindig elárulja, melyik konkrét export felel a méretért.
Nehéz könyvtárak cseréje könnyebb alternatívákra
A bundle optimalizálás leggyorsabb nyeresége gyakran nem a build konfigurációban van, hanem a függőségi listában. A legtöbb projekt 30–50%-os bundle csökkenést érhet el azzal, ha a 2014–2018 között népszerűvé vált nehéz könyvtárakat lecseréli modern, tree shaking-kompatibilis alternatívákra. Volt olyan refactor, ahol pusztán a moment.js kidobása több mint 60 KB-ot vágott le. Egy fél napos munkáért nem rossz hozam.
Régi könyvtár
Méret (min)
Modern alternatíva
Méret (min+tree-shaken)
Megtakarítás
moment.js
67 KB
date-fns vagy Temporal API
5–15 KB
~52 KB
lodash (full)
72 KB
lodash-es nevesített import
2–8 KB
~65 KB
jQuery
87 KB
Vanilla DOM API
0 KB
~87 KB
axios
32 KB
fetch + ky vagy wretch
0–4 KB
~28 KB
chart.js
180 KB
uPlot vagy Apache ECharts (modular)
40–60 KB
~120 KB
uuid
5 KB
crypto.randomUUID()
0 KB
~5 KB
A Bundlephobia minden npm csomaghoz megmutatja a gzip-elt és brotli-elt méretet, a tree-shaken változatot, valamint az alternatívákat. Mielőtt új függőséget adsz a projekthez, érdemes ott ellenőrizni. Az LCP gyorsításához egyébként nemcsak a JS-t, hanem a képeket is érdemes felülvizsgálni; erről bővebben a képoptimalizálás 2026 (AVIF, WebP) cikkben írok.
Brotli tömörítés és CDN beállítások
A legjobb tree shaking és code splitting sem ér semmit, ha a CDN gzip-pel szolgálja ki a fájlokat 2026-ban. A Brotli tömörítés, amelyet minden modern böngésző támogat (96%+ globális lefedettség), átlagosan 17–20%-kal kisebb átviteli méretet ad a Gzip-hez képest azonos CPU költségen. A legtöbb edge CDN (Cloudflare, Fastly, Vercel, AWS CloudFront) alapértelmezetten támogatja, de gyakran csak akkor használja, ha az origin server is brotli encoding-ot ad vissza, vagy ha explicit be van kapcsolva a kompresszió szint 11-re (statikus assetekhez).
A statikus JavaScript chunkokat ideális esetben build időben tömörítjük (Vite-ban a vite-plugin-compression-nel, Webpack-ben a compression-webpack-plugin-nel), nem futási időben, mert a BROTLI_QUALITY_11 szint CPU-igényes. A pre-tömörített .br fájlokat a CDN ezután közvetlenül kiszolgálja a Content-Encoding: br headerrel. Ez a megközelítés és a tartalom-szintű cache stratégia részletesebben szerepel a TTFB optimalizálás és gyorsítótárazási stratégiák cikkben.
Performance budget és Lighthouse CI integráció
Egy optimalizált bundle pár hét alatt visszanőhet eredeti méretére, ha a regressziót nem akadályozzuk meg automatikusan. A performance budget egy explicit méret-limit, amit a CI pipeline minden PR-on érvényesít: ha túllépjük, a build elbukik. A reális 2026-os budget egy SPA-hoz: kezdeti JavaScript chunk < 170 KB (gzip), per-route lazy chunk < 50 KB, total transfer size < 1 MB.
A budget-eket egy GitHub Actions, GitLab CI vagy Vercel preview build során futtatjuk, így minden PR azonnal visszajelzést kap a méret hatásáról. A Lighthouse CI hivatalos repo kiterjedt példákat ad különböző CI rendszerekre. Az interaktivitási regressziók megfogásához érdemes ezt párosítani az INP optimalizálás 2026 cikkben leírt mérési mintákkal. A bundle méret és az INP között szoros korreláció van, mert a parse és compile fázis főszálon történik.
Gyakran ismételt kérdések
Mi a különbség a tree shaking és a code splitting között?
A tree shaking a használaton kívüli kódot távolítja el a végső bundle-ből statikus elemzés alapján, akár egyetlen fájlon belül is. A code splitting ezzel szemben több külön fájlra bontja a kódot, amelyeket a böngésző csak akkor tölt le, amikor szükség van rájuk. A két technika egymást kiegészíti: együtt alkalmazva tipikusan 50–70%-os bundle csökkenést érnek el.
Miért nem működik a tree shaking az alkalmazásomban?
A leggyakoribb okok: a függőség CommonJS formátumban publikál (nem ESM), a sideEffects mező hiányzik vagy true, namespace importot használsz nevesített helyett, vagy a build mód nem production. Ellenőrizd a package.json"type": "module" és "exports" mezőit, és futtass egy bundle analizátort.
Mekkora a maximális JavaScript bundle méret 2026-ban?
A Google PageSpeed Insights ajánlása szerint az első betöltött JavaScript ne haladja meg a 170 KB-ot gzip után, ami nagyjából 500 KB-os feldolgozott kódnak felel meg. Mobil eszközökön ez a Core Web Vitals mutatókhoz szükséges 2,5 másodperces LCP eléréséhez gyakorlatilag szükséges felső határ.
Melyik bundler ajánlott új projekthez 2026-ban?
Új React, Vue vagy Svelte SPA-hoz a Vite a legjobb választás a gyors HMR és Rollup-alapú production build miatt. Next.js projekthez a Turbopack a beépített megoldás. Könyvtár publikálásra a Rollup ad a legtisztább tree shaking eredményt, mert ESM-first és kevesebb runtime kódot generál.
Hogyan tudom méretben elemezni a Next.js bundle-emet?
Telepítsd a @next/bundle-analyzer csomagot, és csomagold be a next.config.js-t a withBundleAnalyzer wrapperrel. A ANALYZE=true npm run build parancs egy interaktív treemap HTML-t generál a .next/analyze/ mappába, ahol látható minden client és server chunk pontos mérete.
A harmadik fél szkriptek (analitika, hirdetések, chat widgetek) a webes teljesítmény rejtett ellenségei. Megmutatjuk, hogyan optimalizáld őket async/defer, facade pattern, Partytown és szerver oldali tagging segítségével — működő kódpéldákkal.