Wydajność

Wydajność JavaScript: praktyczny przewodnik optymalizacji

MZ Maciej Zmitrukiewicz 5 min czytania

JavaScript jest jednym z największych wąskich gardeł wydajności współczesnych stron. Choć częściej mówi się o obrazach, nieoptymalny kod JavaScript może pogarszać Core Web Vitals, szybciej zużywać baterię telefonu i zniechęcać użytkowników, zanim zobaczą treść.


Dlaczego JavaScript jest problemem wydajnościowym?

Przeglądarka pobiera JavaScript, a następnie parsuje, kompiluje i wykonuje kod. Sam transfer pliku to tylko część kosztu.

Plik JavaScript o rozmiarze 300 KB może obciążyć urządzenie bardziej niż obraz o tej samej wadze, ponieważ:

  • obrazy są tylko dekodowane
  • JavaScript aktywnie obciąża CPU

Ma to szczególne znaczenie na urządzeniach z ograniczoną mocą i wolniejszym połączeniem:

  • JS działa na main thread (głównym wątku) — tym samym, który obsługuje renderowanie i interakcje
  • każde zadanie blokujące główny wątek na ponad 50 ms to tzw. Long Task, który pogarsza INP
  • urządzenia mobilne mają CPU 3–5× wolniejsze niż desktop
  • skrypty zewnętrzne konkurują o te same zasoby co Twój kod

Zrozumienie Main Thread

Główny wątek przeglądarki obsługuje renderowanie i dużą część interakcji widocznych dla użytkownika.

  • parsowanie HTML
  • stosowanie CSS
  • wykonywanie JavaScriptu
  • obsługę kliknięć
  • renderowanie pikseli

W danym momencie może wykonywać tylko jedno zadanie.

Jeśli JavaScript zajmie go długim zadaniem, przeglądarka przestaje reagować, INP się pogarsza, a interfejs sprawia wrażenie ociężałego.

Pipeline wydajności wygląda tak:

  1. Przeglądarka pobiera HTML
  2. Zaczyna parsowanie
  3. Trafia na <script> → pauza parsowania HTML
  4. Pobiera i wykonuje JS
  5. Wznawia parsowanie
  6. Renderuje stronę

Skrypt blokujący renderowanie w sekcji <head> może opóźnić LCP.


Jak mierzyć wydajność JavaScript

Chrome DevTools – Performance

Do sprawdzenia faktycznie używanego kodu JavaScript przydaje się narzędzie:

  • Long Tasks (na czerwono > 50 ms)
  • Call Tree – które funkcje są najcięższe
  • aktywność main thread podczas interakcji

Chrome DevTools – Coverage

Coverage pokazuje, jaka część kodu została użyta. Jeśli 80% pliku pozostaje niewykorzystane, usuń niepotrzebny kod albo podziel bundle — zmniejszy to ilość JavaScriptu ładowanego na stronie.

Lighthouse (PageSpeed Insights)

Narzędzie do analizy bundla może dodatkowo wskazać:

  • Reduce unused JavaScript
  • Avoid long main-thread tasks
  • Minify JavaScript

Bundlephobia / webpack-bundle-analyzer

Wizualizacja bundla pokazuje rozmiary bibliotek i pozwala szybko znaleźć te największe.


Code Splitting – ładuj tylko to, co potrzebne

Podział kodu na mniejsze części (code splitting) ogranicza ilość JavaScriptu pobieranego na start.

Zamiast jednego dużego pliku podziel kod na mniejsze moduły i ładuj je dopiero wtedy, gdy funkcja jest potrzebna.

Route-based splitting

// Źle:
import CheckoutPage from './CheckoutPage';

// Dobrze:
const CheckoutPage = () => import('./CheckoutPage');

W popularnych frameworkach można użyć między innymi:

  • Next.js – automatycznie
  • SvelteKit – automatycznie
  • Vite – dynamiczne importy

Component-level splitting

Ciężkie komponenty, które nie są potrzebne od razu, można wczytać dopiero na żądanie:

  • wykresy (np. Chart.js ~200 KB)
  • edytory tekstu
  • modale

Tree Shaking – usuwanie martwego kodu

Tree shaking usuwa nieużywany kod podczas budowania aplikacji, na przykład przez Vite, Webpack lub Rollup.

Aby bundler mógł wykryć nieużywane eksporty, korzystaj z modułów ES (import/export).

Przykład:

// Źle:
import _ from 'lodash';

// Dobrze:
import debounce from 'lodash/debounce';

Pozostałe techniki obejmują:

  • używaj lżejszych bibliotek (np. date-fns zamiast moment.js)
  • usuwaj nieużywane pakiety npm

Strategia ładowania skryptów

Atrybuty i sposób użycia znacznika <script> wpływają na kolejność pobierania oraz wykonania skryptów:

TypZachowanieZastosowanie
<script>blokuje HTMLunikaj w <head>
asyncładuje równolegleanalityka
deferpo HTMLwiększość skryptów
import()na żądaniefunkcje opcjonalne

Unikaj synchronicznych skryptów w <head>, z wyjątkiem tych, które są niezbędne przed renderowaniem.


Web Workers – przenieś pracę poza main thread

Web Workers wykonują JavaScript w tle, dzięki czemu obliczenia nie blokują interfejsu.

Idealne zastosowania:

  • przetwarzanie JSON
  • wyszukiwanie (np. Fuse.js)
  • obróbka obrazów
  • obliczenia
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataset });

// worker.js
self.onmessage = (e) => {
  const result = expensiveCalculation(e.data.data);
  self.postMessage({ result });
};

Biblioteka Comlink upraszcza komunikację z Web Workers.


Scheduler API – oddawanie kontroli przeglądarce

Interfejs scheduler.yield() jest obecnie obsługiwany przez główne przeglądarki i pozwala oddać im czas na inne zadania.

Dzięki temu długie zadania można podzielić na krótsze fragmenty:

async function processItems(items) {
  for (const item of items) {
    processItem(item);
    await scheduler.yield();
  }
}

W części przypadków scheduler.yield() może zastąpić setTimeout(fn, 0) i poprawić INP.


Zarządzanie skryptami zewnętrznymi

Skrypty firm trzecich potrafią mocno zwiększyć koszt działania strony.

Szczególnie dotyczy to:

  • analytics
  • chat
  • reklamy

Takie skrypty mogą dodać nawet 500 KB+ JavaScriptu.

Strategie:

  • audytuj każdy skrypt
  • ładuj po interakcji
  • używaj tag managera z kontrolą
  • hostuj krytyczne skrypty lokalnie
  • ustal performance budget

Checklist optymalizacji JavaScript

Bundle

  • code splitting
  • tree shaking
  • brak nieużywanych pakietów
  • analiza bundle

Ładowanie

  • defer/async
  • lazy loading
  • brak blokujących <script>

Runtime

  • brak Long Tasks > 50 ms
  • Web Workers
  • scheduler.yield()
  • debounce/throttle

Third-party

  • audyt
  • lazy loading
  • performance budget

Mindset wydajności JavaScript

Optymalizację JavaScriptu trzeba powtarzać wraz ze zmianami aplikacji.

Skuteczne zespoły regularnie sprawdzają rozmiar bundla i wpływ skryptów na metryki:

  • mają performance budget w CI/CD
  • monitorują bundle size
  • analizują każdy PR
  • śledzą Core Web Vitals 24/7

Znajomość wydajności JavaScriptu przydaje się każdemu frontend developerowi, nie tylko zespołom dużych platform.


Zacznij od zakładki Coverage w Chrome DevTools. Sprawdź, czy przy pierwszej wizycie 60–80% załadowanego JavaScriptu pozostaje niewykorzystane. Jeśli tak, przeanalizuj ten kod w pierwszej kolejności — jego ograniczenie może poprawić LCP i INP.