Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Ottimizzare il rendering con JavaScript: tecniche e trucchi avanzati

Come ottimizzare il rendering con JavaScript: tecniche e trucchi avanzati con esempi pratici da applicare ai nostri progetti
Come ottimizzare il rendering con JavaScript: tecniche e trucchi avanzati con esempi pratici da applicare ai nostri progetti
Link copiato negli appunti

La velocità di rendering gioca un ruolo cruciale nell'esperienza utente. Una pagina che impiega troppo tempo a caricare, che "salta" durante lo scrolling o che risponde lentamente alle interazioni, rischia di far scappare l'utente verso la concorrenza. In questo articolo vedremo alcune delle tecniche e dei trucchi più avanzati per ottimizzare il rendering tramite JavaScript, riducendo i reflow, i repaint e sfruttando al meglio il browser.

Partiremo da concetti fondamentali come il batching delle mutazioni del DOM, proseguiremo con requestAnimationFrame, IntersectionObserver e debounce/throttle, e toccheremo temi più evoluti come offscreen canvas, web worker e virtual DOM. Ogni tecnica sarà corredata da esempi di codice commentati e spiegati, così da poterti mettere subito al lavoro.

Minimizzare Reflow e Repaint

Che cosa sono:

  • Reflow
  • Repaint

Ogni mutazione del DOM (ad esempio element.style.width = '100px') può innescare un reflow e un repaint. Se fai molte modifiche una a una, il browser ripete il calcolo e il disegno, causando rallentamenti visibili.

Il Batch delle mutazioni raggruppa le modifiche al DOM per evitare layout multipli.

// Esempio: cattiva pratica
for (let i = 0; i  {
  const clone = item.cloneNode(true);
  clone.style.height = (i * 10) + 'px';
  fragment.appendChild(clone);
});
container.innerHTML = '';             // Unico reflow/repaint
container.appendChild(fragment);      // Unico reflow/repaint

Usando DocumentFragment

requestAnimationFrame per animazioni fluide

JavaScript offre setTimeout e setInterval, ma per le animazioni è meglio utilizzare requestAnimationFrame che sincronizza l'esecuzione con il refresh del browser (~60 fps).

function animateBox(timestamp) {
  // Calcola posizione in base al tempo
  box.style.transform = 'translateX(${Math.sin(timestamp/500) * 100}px)';
  requestAnimationFrame(animateBox);
}

// Avvia l'animazione
requestAnimationFrame(animateBox);

Debounce e Throttle per gestire eventi frequenti

Throttle limita la frequenza di chiamata di una funzione. Utile su scroll e resize.

function throttle(fn, limit) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= limit) {
      lastCall = now;
      fn.apply(this, args);
    }
  };
}

window.addEventListener('scroll', throttle(handleScroll, 100));

In questo caso handleScroll

Debounce

Debounce esegue la funzione solo dopo che l'evento ha cessato di verificarsi per un certo intervallo.

function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

window.addEventListener('resize', debounce(handleResize, 200));

handleResize

Intersection Observer per Lazy Loading e Trigger

Invece di raccattare manualmente posizioni con getBoundingClientRect, è possibile sfruttare IntersectionObserver per sapere quando un elemento entra nella viewport.

const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;      // Carica immagine "lazy"
      observer.unobserve(img);        // Non serve più
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img);
});

Come è possibile notare:

  • gli elementi vengono osservati.
  • Quando un elemento è visibile, si carica l'immagine e si rimuove l'osservazione.
  • Offscreen Canva per ottimizzare il rendering

    Se stai gestendo elementi complessi (grafici, filtri immagini), sposta il calcolo su un OffscreenCanvas (supporto in Chrome, Edge).

    // Worker.js
    self.onmessage = e => {
      const offscreen = e.data.canvas;
      const ctx = offscreen.getContext('2d');
      // Disegni intensivi...
      ctx.fillStyle = 'red';
      ctx.fillRect(0,0,200,200);
      self.postMessage('done');
    };
    
    // Main.js
    const worker = new Worker('Worker.js');
    const canvas = document.querySelector('#myCanvas');
    const offscreen = canvas.transferControlToOffscreen();
    worker.postMessage({ canvas: offscreen }, [offscreen]);

    In esso si crea un Worker separato che gestisce il disegno, liberando il thread principale per il rendering.

    Web Worker per calcoli asincroni

    Anche operazioni di calcolo (es. filtri, elaborazioni dati) possono bloccare il thread UI. Spostale in un Web Worker:

    // worker.js
    self.onmessage = e => {
      const result = heavyComputation(e.data);
      self.postMessage(result);
    };
    
    // main.js
    const worker = new Worker('worker.js');
    worker.onmessage = e => renderResult(e.data);
    worker.postMessage(largeDataSet);

    La UI rimane reattiva mentre il Worker elabora dati pesanti.

    Virtual DOM e Framework

    Framework come React, Vue o Svelte usano un Virtual DOM per minimizzare le reali mutazioni del DOM. Il framework calcola le differenze e applica solo i cambiamenti necessari, riducendo reflow/repaint.

    // React example
    function Counter() {
      const [count, setCount] = useState(0);
      return (
        <button> setCount(c => c+1)}>
          Hai cliccato {count} volte
        </button>
      );
    }

    React aggiorna solo il testo interno del pulsante e non ripropone il layout completo.

    Ridurre il Payload di JavaScript

    Un eccessivo download di script rallenta il caricamento iniziale, posticipando l'esecuzione dei vari ottimizzatori. Ecco quindi alcune buone pratiche:

    • Code splitting con Webpack o Rollup.
    • Lazy loading di moduli non critici.
    • Minificazione e gzip/brotli sul server.
    • Performance Budget e Lighthouse

      Stabilisci un performance budget per mantenere dimensioni e tempi di caricamento sotto controllo. Usa Lighthouse (in Chrome DevTools) per misurare metriche come:

      • First Contentful Paint
      • Time to Interactive
      • Total Blocking Time

      Per il monitoraggio in produzione integra invecestrumenti di monitoring real-time (es. New Relic, Sentry) per rilevare rallentamenti su dispositivi reali e ottimizzare dove serve davvero.

      Conclusione: l'importanza di ottimizzare il rendering con JavaScript

      Ottimizzare il rendering con JavaScript non è solo una questione tecnica ma un vero e proprio impegno verso i tuoi utenti. Significa garantire loro un'esperienza fluida, reattiva e senza intoppi, indipendentemente dal dispositivo in uso. Applicando con cura le tecniche illustrate — dal batching delle mutazioni del DOM al corretto uso di requestAnimationFrame, dalla gestione intelligente degli eventi con debounce e throttle fino all'impiego di IntersectionObserver per caricare risorse solo quando servono — potrai ridurre drasticamente i colli di bottiglia che rallentano l'interfaccia.

      Spostare i compiti più pesanti in Web Worker o Offscreen Canvas, adottare un Virtual DOM tramite framework e minimizzare il codice caricato ti permetterà di mantenere il thread principale libero e dedicato all'interazione utente. Questo si traduce in:

      • Riduzione del Time to Interactive
      • Minor consumo di CPU e batteria
      • Esperienza percepita di leggerezza

      Ricorda poi che ogni progetto ha le sue peculiarità: puntare fin da subito ad un performance budget e utilizzare strumenti come Lighthouse o WebPageTest ti aiuterà a misurare i miglioramenti e a concentrarti sulle aree davvero critiche. Integra poi un sistema di monitoraggio in produzione per intercettare eventuali regressioni su dispositivi reali e in condizioni di rete variabili.

      Infine, l'ottimizzazione è un processo continuo. Man mano che la tua applicazione cresce, torna regolarmente su questi principi, rivedi il codice e cerca nuove occasioni per affinare le performance.

      Metti in pratica anche solo alcuni dei trucchi presentati: noterai immediatamente la differenza. Il rendering ottimizzato non è un optional ma un segno di professionalità e cura verso l'utente. Buon lavoro e buon viaggio nel mondo delle prestazioni JavaScript!

Ti consigliamo anche