INP optimalizálás 2026: a felhasználói interakciók sebességének maximalizálása

A weboldalak 43%-ánál az INP nem felel meg a 200 ms-os küszöbértéknek. Gyakorlati útmutató a bundle-elemzés, tree shaking, code-splitting, harmadik fél szkriptek kezelése és modern böngésző API-k alkalmazásához.

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:

  1. 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 a preserveModules beállítás segíthet megőrizni a modulstruktúrát.
  2. 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: false be van állítva a Babel konfigurációban, és a TypeScript "module": "esnext"-tel van konfigurálva.
  3. 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.
  4. 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:

  1. Bundle elemzése: Webpack Bundle Analyzer vagy rollup-plugin-visualizer beállítása és a legnagyobb modulok azonosítása
  2. Nehézsúlyúak cseréje: Moment.js → day.js, lodash → lodash-es célzott importokkal, uuid → crypto.randomUUID()
  3. Tree shaking ellenőrzése: ES Module importok használata, sideEffects konfigurálása a package.json-ban, Babel modules: false-ra állítása
  4. Route-alapú code-splitting: minden route külön chunkként betöltve React.lazy/Suspense-szal vagy dinamikus importokkal
  5. Vendor-splitting konfigurálása: keretrendszerek és nagy könyvtárak saját, hosszú távon gyorsítótárazható chunkokba szervezése
  6. 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
  7. modulepreload kritikus modulokhoz: a legfontosabb modulok deklarálása a <head>-ben rel="modulepreload"-dal
  8. Speculation Rules navigációhoz: fontos következő oldalak előbetöltése vagy előrenderelése a Speculation Rules API-val
  9. 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
  10. 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.

A Szerzőről Editorial Team

Our team of expert writers and editors.