Miért a JavaScript a modern web legnagyobb teljesítményproblémája?
A JavaScript forradalmasította a webet – és egyúttal a legsúlyosabb teljesítmény-szűk keresztmetszetévé is vált. Na de nézzük a számokat: a HTTP Archive Web Almanac 2025 szerint az átlagos asztali kezdőlap immár 697 KB JavaScript-et tölt be, mobilon pedig 632 KB-ot. Az előző évhez képest (620 KB asztali, 570 KB mobil) ez markáns növekedés, ami egyértelmű trendet rajzol ki: a JavaScript gyorsabban nő, mint bármely más erőforrás-kategória a weben.
Ami még riasztóbb: a JavaScript a kérések számát tekintve megelőzte a képeket. A medián asztali oldal ma már 24 JavaScript-fájlt tölt be szemben a 18 képpel. Abszolút bájtban a képek még vezetnek (1.054 KB vs. 613 KB), de a trend egyértelmű – és őszintén szólva a valódi probléma nem is a puszta fájlméretben rejlik.
A JavaScript egyfajta „dupla adózás" alá esik: minden bájtot először le kell tölteni, majd a böngészőnek értelmezni, fordítani és végrehajtani kell. Míg egy 500 KB-os kép letöltés után azonnal megjeleníthető, addig 500 KB JavaScript potenciálisan több száz milliszekundumra blokkolja a fő szálat – és pontosan ezt érzik a felhasználók lomhaságként és válaszkészség-hiányként. Ahogy a HTTP Archive találóan fogalmaz: a JavaScript „olyan teljesítmény-adót hordoz, ami messze meghaladja, amit a fájlmérete sugall".
A Core Web Vitals 2026 útmutatónkban már bemutattuk, hogyan váltotta le az INP (Interaction to Next Paint) 2024 márciusában a jóval gyengébb FID-metrikát mint új válaszkészségi mérőszám. Az INP a teljes felhasználói interakció időtartamát méri – és a túl sok JavaScript a leggyakoribb oka a rossz INP-értékeknek. Ugyanígy, a JavaScript hatással van a Largest Contentful Paint (LCP) értékre is, ha a render-blokkoló szkriptek késleltetik a kritikus tartalmak betöltését.
Ez a cikk az átfogó gyakorlati útmutatód a JavaScript-teljesítmény optimalizálásához. Végigmegyünk a bundle-elemzésen, tree shakingen, code-splittingen, harmadik fél szkriptjeinek kezelésén és a modern böngésző API-kon – konkrét kódpéldákkal, amiket azonnal alkalmazhatsz a projektjeidben.
Bundle-elemzés: mielőtt optimalizálnál, mérj!
A teljesítményoptimalizálás első szabálya: mérj, ne találgass. Mielőtt egyetlen sor kódot megváltoztatnál, meg kell értened, miből áll a JavaScript bundle-öd, melyik modulok foglalnak el mennyi helyet, és hol rejlenek a legnagyobb megtakarítási lehetőségek.
Enélkül vakon optimalizálsz – és kockáztatod, hogy rossz helyre fekteted az idődet.
Webpack Bundle Analyzer
A Webpack Bundle Analyzer a vizuális bundle-elemzés standard eszköze. Interaktív treemap-et generál, ahol minden modul méretarányosan jelenik meg – egyetlen pillantás elég a legnagyobb darabok azonosításához.
# Telepítés
npm install --save-dev webpack-bundle-analyzer
# webpack.config.js-ben
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false,
defaultSizes: 'gzip'
})
]
};
Tipikus felismerések egy bundle-elemzésből:
- Túlméretezett könyvtárak: Moment.js (330 KB) a day.js (2 KB) helyett, teljes lodash (72 KB) a lodash-es tree shakinggel (4–8 KB) helyett, a teljes uuid könyvtár a natív
crypto.randomUUID()helyett - Duplikált függőségek: ugyanannak a könyvtárnak két különböző verziója a bundle-ben – ez gyakori probléma nagyobb projekteknél beágyazott függőségekkel
- Használaton kívüli kód: teljes modulok, amik importálva vannak, de sosem használják őket, vagy feature flagek rég lezárt A/B tesztekhez
- Hiányzó code-splitting lehetőségek: route-specifikus kód a fő bundle-ben, holott csak egyetlen aloldalon kellene
Vite és Rollup: rollup-plugin-visualizer
Ha Vite-ot használsz build eszközként (ami 2026-ban egyre inkább standard), használd helyette a rollup-plugin-visualizer-t:
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
filename: 'dist/bundle-stats.html',
gzipSize: true,
brotliSize: true,
template: 'treemap'
})
]
});
Chrome DevTools: Coverage fül
Egy gyakran figyelmen kívül hagyott eszköz a Chrome DevTools Coverage füle (elérhető a Ctrl+Shift+P → „Show Coverage" menüből). Valós időben mutatja, a letöltött JavaScript kód hány százaléka fut ténylegesen le az oldal betöltésekor. Sok weboldalnál a nem használt kód aránya 50-70 százalék – hatalmas optimalizálási potenciál.
Nyisd meg a Coverage fület, töltsd újra az oldalt, és rendezd „Unused Bytes" szerint. A legnagyobb arányú nem használt kóddal rendelkező fájlok az elsődleges jelöltek a code-splittingre. A Web Almanac 2024 szerint a medián oldalon mintegy 12 KB JavaScript spórolható meg pusztán minifikálással – és a pazarolt bájtok 82,7 százaléka saját (first-party) kódból származik, nem harmadik féltől.
Tree shaking: halott kód automatikus kiirtása
A tree shaking a nem használt kód automatikus eltávolítása a bundler által. A név abból a metaforából ered, hogy megrázunk egy fát, és a halott levelek lehullanak. (Szerintem ez az egyik legszemléletesebb elnevezés a webfejlesztésben.) Ahhoz, hogy a tree shaking működjön, két feltételnek kell teljesülnie: ES Module szintaxis és helyes side-effects konfiguráció.
ES Modules a CommonJS helyett
A tree shaking kizárólag ES Module importokkal (import/export) működik, CommonJS-szel (require/module.exports) nem. Az ok egyszerű: az ES Module importok statikusan elemezhetők – a bundler build-időben pontosan meg tudja állapítani, mely exportok vannak ténylegesen használatban. A CommonJS importok viszont dinamikusak, és futásidőben számíthatók ki, ami lehetetlenné teszi a statikus elemzést.
// ROSSZ: CommonJS – nincs tree shaking
const _ = require('lodash');
const result = _.groupBy(data, 'category');
// → Teljes lodash (72 KB) a bundle-ben
// JÓ: ES Module import – tree shaking működik
import { groupBy } from 'lodash-es';
const result = groupBy(data, 'category');
// → Csak a groupBy és függőségei (~4 KB)
// MÉG JOBB: Közvetlen modul-import
import groupBy from 'lodash-es/groupBy';
const result = groupBy(data, 'category');
// → Minimális lábnyom
Side effects helyes konfigurálása
Még ES Modules-szal sem tudja a bundler eltávolítani a kódot, ha azt feltételezi, hogy az importnak mellékhatásai vannak – vagyis olyan kód, ami a puszta importáláskor dolgokat változtat meg (globális változók, CSS-injektálás, polyfillek). A sideEffects mező a package.json-ben közli a bundlerrel, mely fájlok távolíthatók el biztonságosan:
// package.json – Egyetlen fájlnak sincs mellékhatása
{
"name": "az-en-projektem",
"sideEffects": false
}
// Vagy szelektíven: csak CSS-nek és polyfilleknek van mellékhatása
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
Webpackben a tree shaking production módban automatikusan aktiválódik. A releváns beállítások: usedExports: true (megjelöli a nem használt kódot) és minimize: true (eltávolítja a megjelölt kódot). Emellett a concatenateModules: true (Scope Hoisting) kisebb bundle-öket eredményez a modulok összevonásával:
// webpack.config.js
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
minimize: true,
concatenateModules: true
}
};
Tipikus tree shaking csapdák
A gyakorlatban a tree shaking gyakran ezeken a problémákon bukik el:
- Barrel exportok: Egy
index.js, ami mindent újra-exportál (export * from './module'), megnehezítheti a tree shakinget, mert a bundlernek az összes tranzitív függőséget fel kell oldania. Rollupban apreserveModulesbeállítás segíthet megőrizni a modulstruktúrát. - Transpiler problémák: Ha a Babel vagy TypeScript az ES Modules-t CommonJS-re transpile-olja, elvész a statikus elemezhetőség. Győződj meg róla, hogy a
modules: falsebe van állítva a Babel konfigurációban, és a TypeScript"module": "esnext"-tel van konfigurálva. - UI könyvtárak: Sok komponens-könyvtár, mint az Ant Design vagy Material-UI régebbi verziói, speciális import-stratégiákat igényelnek a hatékony tree shakinghez. Mindig nézd meg az adott UI könyvtár dokumentációját az ajánlott import mintákra.
- Mellékhatásos újra-exportok: Ha egy újra-exportált modulnak önmagában mellékhatásai vannak (pl. globális eseményfigyelőt regisztrál), nem távolítható el tree shakinggel – még akkor sem, ha maga az export nincs használatban.
Code-splitting: a megfelelő mennyiségű kód a megfelelő időben
A code-splitting az a stratégia, hogy a JavaScript bundle-ödet több kisebb chunk-ra bontod, amik igény szerint töltődnek be. Egy monolitikus 500 KB-os bundle helyett, amit teljes egészében le kell tölteni, mielőtt bármi is történne, először csak azt a 80 KB-ot töltöd be, ami az aktuális oldalhoz kell – a többit pedig csak akkor, amikor tényleg szükség van rá.
Iparági tanulmányok azt mutatják, hogy a code-splitting akár 60 százalékkal is csökkentheti a kezdeti betöltési időt. Route-alapú code-splittinggel rendelkező alkalmazásoknál átlagosan 30 százalékos betöltési idő csökkenést mértek a nem felosztott alkalmazásokhoz képest. Ez közvetlen hatással van az LCP-re és a Time to Interactive-ra.
Route-alapú code-splitting
A code-splitting leghatékonyabb és legkevésbé kockázatos formája az útvonalak szerinti felosztás. Az alkalmazásod minden oldala külön chunk lesz, ami csak akkor töltődik be, amikor a felhasználó az adott oldalt meglátogatja.
React, React Router és React.lazy:
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Dinamikus importok külön chunkokat hoznak létre:
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Betöltés...</div>}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Minden import() hívás külön chunkot hoz létre. A böngésző a Dashboard chunkot az első látogatáskor tölti be, a Settings chunkot csak akkor, amikor a felhasználó a beállítások oldalra navigál. Next.js-ben ez a route-alapú code-splitting automatikusan történik: a pages/ vagy app/ könyvtár minden fájlja külön chunk lesz – semmilyen manuális beavatkozás nem szükséges.
Komponens-szintű code-splitting
Teljes oldalak mellett egyes nehéz komponenseket is lazy-loadolhatsz. Ez különösen hasznos olyan elemeknél, amik nem azonnal láthatók, vagy csak felhasználói interakció után jelennek meg – például modálok, diagram-könyvtárak vagy rich text editorok:
import { lazy, Suspense, useState } from 'react';
// Nehéz komponens: diagram könyvtár (~180 KB)
const AnalyticsChart = lazy(
() => import('./components/AnalyticsChart')
);
// Modál gazdag szerkesztővel (~120 KB)
const RichTextEditor = lazy(
() => import('./components/RichTextEditor')
);
function ProductPage() {
const [showChart, setShowChart] = useState(false);
const [showEditor, setShowEditor] = useState(false);
return (
<div>
<h1>Termékrészletek</h1>
<button onClick={() => setShowChart(true)}>
Statisztikák megjelenítése
</button>
{showChart && (
<Suspense fallback={<p>Diagram betöltése...</p>}>
<AnalyticsChart productId={123} />
</Suspense>
)}
<button onClick={() => setShowEditor(true)}>
Leírás szerkesztése
</button>
{showEditor && (
<Suspense fallback={<p>Szerkesztő betöltése...</p>}>
<RichTextEditor />
</Suspense>
)}
</div>
);
}
Ezzel a megközelítéssel a diagram és szerkesztő 300 KB-ja csak akkor töltődik le, amikor a felhasználónak tényleg szüksége van rá. Azoknál, akik csak a termékrészleteket olvassák, ez jelentős sávszélesség- és elemzési idő megtakarítást jelent.
Vendor-splitting az optimális gyorsítótárazásért
Egy másik fontos szempont a vendor-kód (harmadik fél könyvtárai) és az alkalmazáskód szétválasztása. Az ok: a saját forrásfájljaid minden deployment-nél változnak, de a könyvtárak – mint a React, Vue vagy Lodash – gyakran hónapokig ugyanazon a verzión maradnak. Külön chunkokba szétválasztva a böngésző a vendor chunkot hosszú távon gyorsítótárazhatja:
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react-vendor',
chunks: 'all',
priority: 20
},
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10
},
common: {
minChunks: 2,
name: 'common',
chunks: 'all',
priority: 5,
reuseExistingChunk: true
}
}
}
}
};
Vite-ban a chunk-felosztást a Rollup opciókon keresztül konfigurálod:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router': ['react-router-dom'],
'ui-lib': [
'@radix-ui/react-dialog',
'@radix-ui/react-dropdown-menu'
]
}
}
}
}
});
Harmadik fél szkriptjei: a rejtett teljesítmény-időzített bomba
Talán a JavaScript teljesítményproblémák legjobban alábecsült forrása a harmadik fél szkriptjei – tehát az analitika-eszközök, hirdetési platformok, chat widgetek és közösségi média integrációk szkriptjei. A Web Almanac 2025 szerint az összes weboldal több mint 90 százaléka használ legalább egy harmadik fél szkriptet.
A probléma? Ezek a szkriptek 500-1.500 ezredmásodperccel késleltethetik a betöltési időt, és akár 1.640 ezredmásodpercre is blokkolhatják a fő szálat. Különösen problémásak a Tag Managerek, amik maguk is számos további szkriptet töltenek be, valamint a Customer Success szkriptek (chat-megoldások), amik általában átlagon felül nehezek. A saját kódoddal ellentétben ezek minősége és mérete felett csak korlátozott kontrolllal rendelkezel.
A Web Almanac 2025 a Core Web Vitals-szal való közvetlen összefüggést is kimutatja: a leglátogatottabb weboldalak komplex harmadik fél integrációkkal – bár a jó INP-értékek aránya 53-ról 63 százalékra nőtt a top 1.000 weboldalnál – továbbra is a legnagyobb nehézségekkel küzdenek a válaszkészség terén. Több JavaScript, több funkcionalitás, több harmadik fél integráció = nehezebb INP-optimalizálás.
async vs. defer: a kontroll minimuma
Nem kritikus harmadik fél szkriptjei soha nem szabad, hogy render-blokkolóan töltődjenek be. Az async és defer attribútumok megakadályozzák ezt – de alapvetően különböznek a viselkedésükben:
<!-- ROSSZ: Render-blokkoló -->
<script src="https://example.com/analytics.js"></script>
<!-- async: Letöltés párhuzamosan, végrehajtás azonnal letöltés után
Jó: független szkriptekhez, mint az analitika -->
<script async src="https://example.com/analytics.js">
</script>
<!-- defer: Letöltés párhuzamosan, végrehajtás a HTML feldolgozás után
Jó: DOM-ot használó szkriptekhez -->
<script defer src="https://example.com/widget.js">
</script>
Az ökölszabály: használj defer-t alapértelmezettként a legtöbb harmadik fél szkripthez. Az async olyan szkriptekhez alkalmas, amik valóban függetlenek a DOM-tól és egymástól, mint a tisztán analitikai kódrészletek. És ne feledd: az async szkriptek is blokkolhatják a fő szálat – csak a letöltésük nem render-blokkoló.
A Facade minta: Import on Interaction
Nehéz beágyazásokat – mint YouTube videók, chat widgetek vagy közösségi média feedek – nem kell azonnal betölteni. A Facade minta (más néven „Import on Interaction") egy könnyű helyettesítővel váltja ki őket, ami csak felhasználói interakcióra tölti be a valódi widgetet:
function YouTubeFacade({ videoId, title }) {
const [isLoaded, setIsLoaded] = useState(false);
if (isLoaded) {
return (
<iframe
width="560"
height="315"
src={`https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1`}
title={title}
allow="autoplay; encrypted-media"
allowFullScreen
/>
);
}
return (
<button
onClick={() => setIsLoaded(true)}
className="youtube-facade"
style={{
backgroundImage:
`url(https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg)`
}}
aria-label={`Videó lejátszása: ${title}`}
>
<svg viewBox="0 0 68 48" width="68" height="48">
<path d="M66.5 7.7c-.8-2.9-2.5-5.4-5.4-6.2
C55.8.1 34 0 34 0S12.2.1 6.9 1.6c-3 .7-4.6
3.3-5.4 6.1C.1 13 0 24 0 24s.1 11 1.5 16.3
c.8 2.9 2.5 5.4 5.4 6.2C12.2 47.9 34 48 34
48s21.8-.1 27.1-1.6c3-.7 4.6-3.3 5.4-6.1
C67.9 35 68 24 68 24s-.1-11-1.5-16.3z"
fill="red"/>
<path d="M45 24L27 14v20" fill="white"/>
</svg>
</button>
);
}
Ezzel a mintával jellemzően 500 KB-tól 1 MB-ig spórolhatsz JavaScript-et YouTube beágyazásonként, ami csak akkor töltődik be, ha a felhasználó tényleg rákattint a videóra. A lite-youtube-embed könyvtár kész, produkciós implementációt kínál ebből a mintából.
Web Worker harmadik fél szkriptjeihez: Partytown
Egy igazán innovatív megközelítés a harmadik fél szkriptjeinek áthelyezése egy Web Workerbe, hogy a fő szál teljesen tehermentesíthető legyen. A Partytown könyvtár pontosan ezt csinálja – és a beállítása meglepően egyszerű:
<script>
partytown = {
forward: ['dataLayer.push', 'gtag'],
debug: false
};
</script>
<script src="/~partytown/partytown.js"></script>
<!-- type="text/partytown" a type="text/javascript" helyett -->
<script type="text/partytown"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX">
</script>
<script type="text/partytown">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXX');
</script>
A Partytown az analitika, Tag Manager és hirdető szkriptek végrehajtását teljesen egy külön szálba helyezi. A fő szál szabad marad a rendereléshez és a felhasználói interakciókhoz – mérhető javulással az INP és a Total Blocking Time terén. Viszont fontos megjegyezni, hogy a Partytown nem minden szkriptre alkalmas: azok a szkriptek, amik közvetlenül és szinkron módon hozzáférnek a DOM-hoz, problémákat okozhatnak.
Modern böngésző API-k JavaScript-betöltéshez
2026-ban több erőteljes böngésző API áll rendelkezésedre, amik alapjaiban változtatják meg a JavaScript betöltését és végrehajtását. Három közülük különös figyelmet érdemel.
Import Maps: modulok feloldása bundler nélkül
Az Import Maps 2025 óta minden nagy böngészőben elérhető, és lehetővé teszi az úgynevezett „bare specifierek" közvetlen feloldását a böngészőben – bundler nélkül. Ez korábban a build-eszközök kizárólagos funkciója volt. Az Import Maps különösen kisebb projekteknél, prototípusoknál és progresszív fejlesztésnél hasznos:
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"preact/hooks": "https://esm.sh/[email protected]/hooks",
"htm": "https://esm.sh/[email protected]"
}
}
</script>
<script type="module">
import { h, render } from 'preact';
import { useState } from 'preact/hooks';
import htm from 'htm';
const html = htm.bind(h);
function Counter() {
const [count, setCount] = useState(0);
return html`<button onClick=${() => setCount(count + 1)}>
Kattintások: ${count}
</button>`;
}
render(html`<${Counter} />`, document.getElementById('app'));
</script>
Az Import Maps támogatja a Scope-okat (különböző modulverziók útvonalonként) és az Integrity hash-eket a dinamikusan betöltött modulok biztonsági validálásához. Produkciós alkalmazásoknál build pipeline-nal a bundlerek továbbra is jobb választás, de az Import Maps elegáns zero-build megoldást kínál bizonyos felhasználási esetekre.
modulepreload: modulok előzetes betöltése és fordítása
A <link rel="modulepreload"> attribútum túlmutat az egyszerű előbetöltésen: utasítja a böngészőt, hogy egy JavaScript modult ne csak töltsön le, hanem azonnal elemezze és fordítsa le. Amikor a modul ténylegesen végrehajtódik, már készen áll – az egyébként felmerülő elemzési és fordítási idő teljesen kiesik.
<head>
<!-- Kritikus modulok előzetes betöltése ÉS fordítása -->
<link rel="modulepreload" href="/js/app.js">
<link rel="modulepreload" href="/js/router.js">
<link rel="modulepreload" href="/js/store.js">
</head>
<!-- A modul végrehajtáskor már le van fordítva -->
<script type="module" src="/js/app.js"></script>
A döntő előny a rel="preload"-hoz képest: a modulepreload-nál a végrehajtás pillanatában teljesen kiesik az elemzési és fordítási idő. Nagy moduloknál ez 100-300 ezredmásodperc megtakarítást jelenthet. Ráadásul a böngésző automatikusan előtöltheti a modul függőségeit is – sima preload-nál minden függőséget manuálisan kellene deklarálnod.
Speculation Rules API: a következő oldal előzetes renderelése
A Speculation Rules API lehetővé teszi, hogy teljes oldalakat előre betölts (prefetch) vagy akár teljesen előre renderelj (prerender) – mielőtt a felhasználó rákattintana. Ez nem egyes erőforrások prefetch-e, hanem egy teljes navigáció előkészítése. A hatás lenyűgöző: az előre renderelt oldalak gyakorlatilag azonnal megjelennek – mintha a felhasználó már megnyitott fülek között váltana.
<script type="speculationrules">
{
"prerender": [
{
"source": "document",
"where": {
"and": [
{ "href_matches": "/termekek/*" },
{ "not": { "selector_matches": ".no-prerender" } }
]
},
"eagerness": "moderate"
}
],
"prefetch": [
{
"source": "document",
"where": { "href_matches": "/blog/*" },
"eagerness": "conservative"
}
]
}
</script>
Az eagerness szintek szabályozzák, mennyire agresszívan spekulál a böngésző:
- immediate: Azonnal a szabály felismerése után spekulál
- eager: A lehető legkorábban, de a böngésző belátása szerint
- moderate: A link fölé hoverelésnél – jó kompromisszum a sebesség és a sávszélesség-felhasználás között
- conservative: Csak egérlenyomásnál vagy érintésnél – minimális sávszélesség-felhasználás, de a legrövidebb előkészítési idő is
Fontos: A Speculation Rules API-t jelenleg teljes mértékben csak a Chromium böngészők (Chrome, Edge) támogatják. Más böngészők egyszerűen figyelmen kívül hagyják a szabályokat – tehát nincs hátránya a progresszív fejlesztésnek. Az API a <link rel="prerender"> utódjaként készült, és jóval több kontrollt és rugalmasságot kínál elődjénél.
Bundler-választás 2026-ban: Vite, Webpack és a Rolldown-forradalom
A megfelelő bundler választása hatalmas hatással van a build-teljesítményre és a generált bundle-ök minőségére. 2026-ban egy izgalmas újdonság uralja a képet.
Vite: az új standard
Vite az új webprojektek de facto standardjává vált. Fejlesztői módban natív ES Modules-t és esbuild-et használ a villámgyors Hot Module Replacement-hez (HMR), produkciós buildekhez Rollupra támaszkodik – kiváló tree shakinggel és átlagosan mintegy 13 százalékkal kisebb bundle-ökkel, mint a Webpack-ekvivalensek. A konfiguráció minimális, az ökoszisztéma rohamosan nő, és a fejlesztői élmény egyszerűen kiváló.
Rolldown + Oxc: a fordulópont
A build-eszköz világának legizgalmasabb fejleménye a Rolldown – egy Rustban írt bundler, amit Rollup-kompatibilis helyettesítőnek terveztek, és ami már kísérleti backendként integrálódik a Vite-ba. A benchmark-eredmények lenyűgözőek: a GitLab rolldown-vite-tal 2,6-szer gyorsabb buildeket ért el, mint a standard Vite-tal. Minden natív plugin aktiválásával a build-idő 2,5 percről mindössze 22 másodpercre csökkent – ez 43-szor gyorsabb, mint a meglévő Webpack build.
Az Oxc minifierrel (szintén Rustban írva) kombinálva a Rolldown 18 százalékkal kisebb bundle-öket generál, mint a Rollup-ekvivalensek. A kulcs a többszörös dead-code elimination lépésekben rejlik, hasonlóan a Rolluphoz, de minimális teljesítmény-overhead-del a natív Rust implementációnak köszönhetően.
Webpack: továbbra is releváns
A Webpack meglévő projekteknél és komplex vállalati alkalmazásoknál továbbra is releváns. A konfigurálhatósága felülmúlhatatlan, a plugin-ökoszisztéma hatalmas, és specifikus igényeknél (Module Federation, egyedi loader-láncok) gyakran nincs alternatíva. Új projekteknél viszont 2026-ban a Vite-ot javaslom – a fejlesztői élmény és a bundle-minőség egyértelműen mellette szól.
Teljesítmény-költségvetések: korlátok beállítása és automatikus betartatása
Az összes eddigi optimalizálás hosszú távon keveset ér, ha új funkciók és könyvtárak kontrollálatlanul újra felfújják a JavaScript-méretet. A teljesítmény-költségvetések rögzített felső határokat állítanak be, amiket a CI/CD pipeline automatikusan ellenőriz – és elbuktatja a buildet, ha túllépik őket.
Költségvetések a build konfigurációban
A Webpack beépített teljesítmény-költségvetéseket kínál, amiket közvetlenül a konfigurációban definiálhatsz:
// webpack.config.js
module.exports = {
performance: {
maxAssetSize: 250000,
maxEntrypointSize: 300000,
hints: 'error'
}
};
Lighthouse CI a pipeline-ban
Átfogó teljesítmény-felügyelethez integráld a Lighthouse CI-t a CI/CD pipeline-odba. Így minden pull requestben felismered a teljesítmény-regressziókat – nem csak a deployment után:
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: pull_request
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci && npm run build
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v12
with:
configPath: .lighthouserc.json
uploadArtifacts: true
// .lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.9 }],
"total-byte-weight": [
"error", { "maxNumericValue": 500000 }
],
"mainthread-work-breakdown": [
"warn", { "maxNumericValue": 4000 }
],
"interactive": [
"error", { "maxNumericValue": 3500 }
]
}
}
}
}
Emellett az olyan eszközök, mint a Codecov Bundle Analysis, lehetővé teszik a bundle-méret változások közvetlen megjelenítését a pull requestekben – így a fejlesztők azonnal látják, ha a módosításaik túllépnék a költségvetést.
Gyakorlati ellenőrzőlista: JavaScript-teljesítmény 10 lépésben
Befejezésül egy priorizált ellenőrzőlista, amit kiindulópontként használhatsz az optimalizáláshoz. Dolgozd végig fentről lefelé – a pontok hatékonyság és ráfordítás szerint vannak rendezve:
- Bundle elemzése: Webpack Bundle Analyzer vagy rollup-plugin-visualizer beállítása és a legnagyobb modulok azonosítása
- Nehézsúlyúak cseréje: Moment.js → day.js, lodash → lodash-es célzott importokkal, uuid →
crypto.randomUUID() - Tree shaking ellenőrzése: ES Module importok használata,
sideEffectskonfigurálása a package.json-ban, Babelmodules: false-ra állítása - Route-alapú code-splitting: minden route külön chunkként betöltve
React.lazy/Suspense-szal vagy dinamikus importokkal - Vendor-splitting konfigurálása: keretrendszerek és nagy könyvtárak saját, hosszú távon gyorsítótárazható chunkokba szervezése
- Harmadik fél szkriptjeinek auditálása: minden külső szkript leltározása, async/defer használata, Facade-ek implementálása nehéz beágyazásokhoz
- modulepreload kritikus modulokhoz: a legfontosabb modulok deklarálása a
<head>-benrel="modulepreload"-dal - Speculation Rules navigációhoz: fontos következő oldalak előbetöltése vagy előrenderelése a Speculation Rules API-val
- Teljesítmény-költségvetések beállítása: fix felső korlátok a bundle-méretekre a CI/CD pipeline-ban betartatva
- Folyamatos mérés: Real User Monitoring a Web Vitals könyvtárral, Lighthouse CI a pipeline-ban, rendszeres bundle-auditok
Összefoglalás: a leggyorsabb JavaScript az, amit nem szállítasz ki
A JavaScript-világ rohamosan fejlődik. A Rolldown és Oxc forradalmi build-eszközök küszöbén állunk, amik a build-időket akár 40-szeresére és azon túl is csökkenthetik. Az Import Maps és a Speculation Rules API megváltoztatja, hogyan töltünk be modulokat és gyorsítjuk a navigációt. Az INP mint Core Web Vital metrika pedig arra kényszerít minket, hogy újragondoljuk a teljes JavaScript-életciklust – az első letöltéstől az elemzésen és fordításon át minden egyes felhasználói interakció végrehajtásáig.
De minden technológiai lelkesedés mellett egy egyszerű igazság áll: a leggyorsabb JavaScript az, amit nem szállítasz ki. Kezdd minden optimalizálást azzal a kérdéssel: „Tényleg szükségünk van erre a kódra?" – és aztán haladj végig a cikkben bemutatott technikákon. A bundle-elemzés, tree shaking, intelligens code-splitting és a harmadik fél szkriptjeinek tudatos kezelése együttesen 50-70 százalékkal csökkentheti a weboldalad JavaScript-terhelését – közvetlen, mérhető hatással az LCP-re, INP-re és az egész felhasználói élményre.
A teljesítményoptimalizálás nem egyszeri projekt, hanem folyamatos folyamat. Állíts be teljesítmény-költségvetéseket, automatizáld az auditjaidat, és tedd a JavaScript-teljesítményt a fejlesztési munkafolyamatod szerves részévé. Mert végső soron nem a Lighthouse-pontszámokról vagy technikai metrikákról van szó – hanem arról, hogy a felhasználóid egy gyors, reszponzív weboldalt tapasztaljanak, amit szívesen látogatnak és szívesen visszatérnek.