Ottimizza l'esecuzione di JavaScript

Spesso JavaScript attiva modifiche visive. A volte si tratta direttamente di manipolazioni dello stile, mentre a volte si tratta di calcoli che generano modifiche visive, come la ricerca o l'ordinamento dei dati. JavaScript di lunga durata o non corretto è una causa comune di problemi di prestazioni. Cerca di ridurne l'impatto laddove possibile.

Paul Lewis
Paul Lewis

Spesso JavaScript attiva modifiche visive. A volte si tratta direttamente di manipolazioni degli stili, mentre a volte si tratta di calcoli che determinano modifiche visive, come la ricerca o l'ordinamento dei dati. JavaScript a lunga esecuzione o con tempi non corretti è una causa comune di problemi di prestazioni. Cerca di ridurne l'impatto laddove possibile.

La profilazione delle prestazioni di JavaScript può essere qualcosa di originale, perché il codice JavaScript che scrivi non ha nulla a che fare con il codice che viene effettivamente eseguito. I browser moderni utilizzano i compilatori JIT e tutti i tipi di ottimizzazioni e trucchi per provare a eseguire l'esecuzione più rapida possibile e questo cambia sostanzialmente la dinamica del codice.

Detto questo, tuttavia, ci sono alcune cose che puoi sicuramente fare per consentire alle tue app di eseguire correttamente JavaScript.

Riepilogo

  • Evita setTimeout o setInterval per gli aggiornamenti visivi; usa sempre requestAnimationFrame.
  • Sposta il codice JavaScript di lunga durata dal thread principale ai web worker.
  • Utilizza le microattività per apportare modifiche al DOM in più frame.
  • Utilizza la sequenza temporale di Chrome DevTools e lo strumento JavaScript Profiler per valutare l'impatto di JavaScript.

Utilizza requestAnimationFrame per i cambiamenti visivi

Quando sullo schermo si verificano cambiamenti visivi, vuoi lavorare al momento giusto per il browser, ovvero esattamente all'inizio dell'inquadratura. L'unico modo per garantire che il codice JavaScript venga eseguito all'inizio di un frame è utilizzare requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

I framework o gli esempi potrebbero utilizzare setTimeout o setInterval per apportare modifiche visive come le animazioni, ma il problema è che il callback viene eseguito in un certo punto del frame, possibilmente alla fine, e questo può spesso causare la perdita di un frame, causando interruzioni.

setTimeout che causa la perdita di un frame da parte del browser.

Infatti, jQuery utilizzava setTimeout per il suo comportamento animate. Nella versione 3 è stato modificato per utilizzare requestAnimationFrame. Se utilizzi una versione precedente di jQuery, puoi applicare la patch in modo che utilizzi requestAnimationFrame, che è vivamente consigliato.

Riduci la complessità o utilizza web worker

JavaScript viene eseguito nel thread principale del browser, insieme a calcoli di stile, layout e, in molti casi, Paint. Se JavaScript viene eseguito per molto tempo, blocca queste altre attività, causando potenzialmente la perdita di frame.

Devi stabilire con tattica quando viene eseguito JavaScript e per quanto tempo. Ad esempio, se utilizzi un'animazione come lo scorrimento, dovresti cercare di mantenere JavaScript entro i limiti di 3-4 ms. Se passa troppo tempo, c'è il rischio di impiegare troppo tempo. Se ti trovi in un periodo di inattività, puoi permetterti di dedicarti di più al tempo che ti serve.

In molti casi è possibile spostare il lavoro di calcolo puro ai web worker se, ad esempio, non richiede l'accesso al DOM. La manipolazione o il trasferimento dei dati, come l'ordinamento o la ricerca, sono spesso adatti a questo modello, come il caricamento e la generazione del modello.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

Non tutto il lavoro è adatto a questo modello: i web worker non hanno accesso al DOM. Dove il tuo lavoro deve trovarsi nel thread principale, prendi in considerazione un approccio di raggruppamento, in cui l'attività più grande debba essere segmentata in microattività, ognuna delle quali non richiede più di qualche millisecondo, ed eseguita all'interno dei gestori requestAnimationFrame su ciascun frame.

Questo approccio comporta conseguenze in termini di UX e UI e dovrai assicurarti che l'utente sappia che un'attività è in fase di elaborazione utilizzando un indicatore di avanzamento o di attività. In ogni caso, questo approccio manterrà libero il thread principale della tua app, aiutandola a rispondere alle interazioni degli utenti.

Conoscere la "frame tax" di JavaScript

Quando valuti un framework, una libreria o il tuo codice, è importante valutare quanto costa eseguire il codice JavaScript frame per frame. Questo è particolarmente importante quando si svolgono attività di animazione critiche per le prestazioni, come la transizione o lo scorrimento.

Il riquadro Prestazioni di Chrome DevTools è il modo migliore per misurare il costo di JavaScript. In genere ottieni record di basso livello come questo:

Una registrazione delle prestazioni in Chrome DevTools

La sezione Principale fornisce un grafico a fiamme delle chiamate JavaScript che ti consente di analizzare esattamente quali funzioni sono state chiamate e quanto tempo è stato necessario.

Grazie a queste informazioni, puoi valutare l'impatto delle prestazioni di JavaScript sulla tua applicazione e iniziare a individuare e correggere eventuali hotspot in cui l'esecuzione delle funzioni richiede troppo tempo. Come accennato in precedenza, dovresti cercare di rimuovere il codice JavaScript a lunga esecuzione o, se non è possibile, spostarlo su un web worker liberando il thread principale per continuare con le altre attività.

Consulta la sezione Introduzione all'analisi delle prestazioni del runtime per scoprire come utilizzare il riquadro Prestazioni.

Evita la microottimizzazione di JavaScript

Potrebbe essere interessante sapere che il browser è in grado di eseguire una versione di qualcosa 100 volte più velocemente di un'altra, ad esempio la richiesta del offsetTop di un elemento è più veloce dell'elaborazione di getBoundingClientRect(). Tuttavia, è quasi sempre vero che richiamerai funzioni come queste solo poche volte per frame, quindi di solito concentrarsi su questo aspetto delle prestazioni di JavaScript è uno spreco di lavoro. In genere risparmi solo frazioni di millisecondi.

Se stai creando un gioco o un'applicazione costosa dal punto di vista computazionale, probabilmente sei un'eccezione a queste indicazioni, poiché in genere molte risorse di calcolo in un singolo frame e, in quel caso, tutto è utile.

In breve, dovresti fare molta attenzione alle micro-ottimizzazione, perché in genere non sono mappate al tipo di applicazione che stai creando.