Salta al contenuto
JavaScript Pubblicato il · ~12 min

Panoramica

Le performance JavaScript incidono direttamente sulla percezione di velocità e sulla conversione. In questa guida vediamo come ridurre il lavoro sul main thread con code splitting e lazy‑loading, rendere le interazioni fluide con debounce/throttle e misurare le metriche chiave come LCP e INP.

Caricamento e code splitting

Carica solo ciò che serve, quando serve. Usa import() dinamico per suddividere il bundle per route/feature e caricare on‑demand.

Dynamic import su azione utente

document.getElementById('open-chart').addEventListener('click', async () => {
  const { renderChart } = await import(/* webpackChunkName: "chart" */ './chart.js');
  renderChart();
});

Lazy‑loading con IntersectionObserver

const el = document.querySelector('#reviews-widget');
const io = new IntersectionObserver(async ([entry], obs) => {
  if (!entry.isIntersecting) return;
  const mod = await import('./reviews-widget.js');
  mod.mount(el);
  obs.disconnect();
});
io.observe(el);

Suggerimenti: evita side‑effect globali nei moduli caricati on‑demand, esporta funzioni pure; usa preload/prefetch per migliorare la percezione nelle navigazioni successive.

Interazioni fluide: debounce e throttle

Riduci il lavoro per eventi ad alta frequenza (scroll, resize, input) con funzioni di controllo del rate.

Debounce

function debounce(fn, wait = 200) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn.apply(null, args), wait);
  };
}

const onSearch = debounce((q) => fetch('/api?q='+encodeURIComponent(q)), 300);
const input = document.querySelector('#search');
input.addEventListener('input', (e) => onSearch(e.target.value));

Throttle

function throttle(fn, limit = 100) {
  let last = 0, timer;
  return (...args) => {
    const now = Date.now();
    if (now - last >= limit) {
      last = now;
      fn.apply(null, args);
    } else if (!timer) {
      const remaining = limit - (now - last);
      timer = setTimeout(() => {
        last = Date.now();
        timer = null;
        fn.apply(null, args);
      }, remaining);
    }
  };
}

window.addEventListener('scroll', throttle(() => {
  // lavoro leggero: aggiornare progress bar, ecc.
}, 100));

Preferisci operazioni idempotenti e evita layout thrashing (misurazioni/forzature di layout ripetute). Usa requestAnimationFrame per aggiornamenti visivi.

Misurare e monitorare le performance

Misura prima di ottimizzare. Traccia le Core Web Vitals e osserva gli impatti sul main thread.

Osservare LCP

new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const last = entries[entries.length - 1];
  console.log('LCP', last.startTime.toFixed(0), 'ms');
}).observe({ type: 'largest-contentful-paint', buffered: true });

Osservare INP

new PerformanceObserver((entryList) => {
  for (const e of entryList.getEntries()) {
    if (e.name !== 'click' && e.name !== 'keydown' && e.name !== 'pointerdown') continue;
    console.log('INP candidate', Math.max(e.processingStart - e.startTime, e.duration).toFixed(0), 'ms');
  }
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });

In produzione usa web-vitals per calcolare INP finale e invia i dati a un endpoint RUM per ottimizzazioni iterative.

Checklist rapida

  • Analizza il bundle: rimuovi dipendenze superflue e attiva minification/treeshaking
  • Applica code splitting per route/feature e lazy‑loading con import()
  • Debounce/throttle per eventi ad alta frequenza (scroll, resize, input)
  • Evita lavoro pesante sul main thread; delega a Web Worker quando possibile
  • Monitora LCP/INP e correggi regressioni in CI/CD

FAQ

Che differenza c’è tra debounce e throttle?

Debounce posticipa l’esecuzione finché l’utente non si ferma; throttle limita la frequenza massima in un intervallo. Entrambi riducono il carico sul main thread.

Quando conviene il lazy‑loading?

Per widget fuori viewport, grafici, editor ricchi o route non essenziali al primo paint. Migliora TTI/INP percepita.

Come misuro LCP/INP in produzione?

Usa la libreria web-vitals per ottenere valori standardizzati e inviali a un endpoint di raccolta (RUM) per analisi.


Commenti

I commenti sono moderati tramite Cusdis e hanno il solo scopo di arricchire l’articolo: resta in tema e aggiungi valore. Grazie!