Migliorare le prestazioni della tua app HTML5

Introduzione

HTML5 ci offre ottimi strumenti per migliorare l'aspetto visivo delle applicazioni web. Questo vale in modo particolare per le animazioni. Tuttavia, con questo nuovo potere arrivano anche nuove sfide. In realtà, queste sfide non sono poi così nuove e a volte potrebbe avere senso chiedere al tuo vicino di scrivania, il programmatore Flash, come ha superato problemi simili in passato.

Ad ogni modo, quando lavori con le animazioni, è estremamente importante che gli utenti le percepiscano come fluide. Dobbiamo capire che la fluidità delle animazioni non può essere creata semplicemente aumentando i fotogrammi al secondo oltre qualsiasi soglia cognitiva. Purtroppo il nostro cervello è più intelligente di così. Scoprirai che 30 fotogrammi di animazione al secondo (fps) sono molto meglio di 60 fps con solo alcuni fotogrammi persi nel mezzo. Le persone odiano le punte.

Questo articolo cercherà di fornirti gli strumenti e le tecniche per migliorare l'esperienza della tua applicazione.

La strategia

Non vogliamo scoraggiarti dal creare app straordinarie e visivamente sbalorditive con HTML5.

Quando noti che il rendimento potrebbe essere un po' migliore, torna qui e scopri come migliorare gli elementi della tua applicazione. Naturalmente, può essere utile fare subito le cose per bene, ma non lasciare che questo ti impedisca di essere produttivo.

Fidelity visiva++ con HTML5

Accelerazione hardware

L'accelerazione hardware è un traguardo importante per le prestazioni complessive del rendering nel browser. Lo schema generale è scaricare le attività che altrimenti verrebbero calcolate dalla CPU principale all'unità di elaborazione grafica (GPU) nell'adattatore grafico del computer. Ciò può comportare enormi miglioramenti delle prestazioni e può anche ridurre il consumo di risorse sui dispositivi mobili.

Questi aspetti del documento possono essere accelerati dalla GPU

  • Composizione del layout generale
  • Transizioni CSS3
  • Trasformazioni 3D CSS3
  • Disegno su tela
  • Disegno 3D WebGL

Sebbene l'accelerazione di Canvas e WebGL siano funzionalità speciali che potrebbero non essere applicabili alla tua applicazione specifica, i primi tre aspetti possono aiutare praticamente ogni app a diventare più veloce.

Cosa può essere accelerato?

L'accelerazione GPU funziona trasferendo attività specifiche e ben definite ad hardware a scopo speciale. Lo schema generale prevede che il documento venga suddiviso in più "livelli" invariati rispetto agli aspetti della pagina accelerati. Questi livelli vengono visualizzati utilizzando la pipeline di rendering tradizionale. La GPU viene poi utilizzata per comporre i livelli in una singola pagina applicando gli "effetti" che possono essere accelerati in tempo reale. Un possibile risultato è che un oggetto animato sullo schermo non richiede un singolo "riarrangiamento" della pagina durante l'animazione.

Ciò che devi ricordare è che devi consentire al motore di rendering di identificare facilmente quando può applicare la sua magia di accelerazione della GPU. Considera l'esempio seguente:

Sebbene funzioni, il browser non sa realmente che stai eseguendo qualcosa che dovrebbe essere percepito come un'animazione fluida da un essere umano. Considera cosa succede quando ottieni la stessa estetica utilizzando le transizioni CSS3:

Il modo in cui il browser implementa questa animazione è completamente nascosto allo sviluppatore. Ciò significa che il browser è in grado di applicare trucchi come l'accelerazione della GPU per raggiungere l'obiettivo definito.

Esistono due flag a riga di comando utili per Chrome che aiutano a eseguire il debug dell'accelerazione della GPU:

  1. --show-composited-layer-borders mostra un bordo rosso attorno agli elementi che vengono manipolati a livello di GPU. Utile per verificare che le manipolazioni vengano eseguite all'interno del livello GPU.
  2. --show-paint-rects tutte le modifiche non GPU vengono dipinte e viene visualizzato un bordo chiaro attorno a tutte le aree che vengono ridipinte. Puoi vedere il browser che ottimizza le aree di pittura in azione.

Safari ha flag di runtime simili descritti qui.

Transizioni CSS3

Le transizioni CSS semplificano l'animazione degli stili per tutti, ma sono anche una funzionalità di rendimento intelligente. Poiché una transizione CSS è gestita dal browser, la fedeltà della relativa animazione può essere notevolmente migliorata e, in molti casi, accelerata dall'hardware. Attualmente WebKit (Chrome, Safari, iOS) dispone di trasformazioni CSS con accelerazione hardware, ma questa funzionalità sarà presto disponibile su altri browser e piattaforme.

Puoi utilizzare gli eventi transitionEnd per creare script in combinazioni efficaci, anche se al momento la registrazione di tutti gli eventi di fine transizione supportati significa monitorare webkitTransitionEnd transitionend oTransitionEnd.

Molte librerie hanno ora introdotto API di animazione che sfruttano le transizioni, se presenti, e in caso contrario utilizzano l'animazione in stile DOM standard. scripty2, transizione YUI, jQuery animate enhanced.

Traslazione CSS3

Sicuramente ti è capitato di animare la posizione x/y di un elemento nella pagina. Probabilmente hai manipolato le proprietà left e top dello stile in linea. Con le trasformazioni 2D, possiamo utilizzare la funzionalità translate() per replicare questo comportamento.

Possiamo combinarlo con l'animazione DOM per ottenere il massimo

<div style="position:relative; height:120px;" class="hwaccel">

  <div style="padding:5px; width:100px; height:100px; background:papayaWhip;
              position:absolute;" id="box">
  </div>
</div>

<script>
document.querySelector('#box').addEventListener('click', moveIt, false);

function moveIt(evt) {
  var elem = evt.target;

  if (Modernizr.csstransforms && Modernizr.csstransitions) {
    // vendor prefixes omitted here for brevity
    elem.style.transition = 'all 3s ease-out';
    elem.style.transform = 'translateX(600px)';

  } else {
    // if an older browser, fall back to jQuery animate
    jQuery(elem).animate({ 'left': '600px'}, 3000);
  }
}
</script>

Utilizziamo Modernizr per eseguire il test delle funzionalità per le trasformazioni 2D CSS e le transizioni CSS. In questo caso, utilizzeremo translate per modificare la posizione. Se l'animazione viene utilizzata con una transizione, è probabile che il browser possa applicare l'accelerazione hardware. Per dare un'altra spinta al browser nella giusta direzione, utilizzeremo l'elemento "magic CSS bullet" riportato sopra.

Se il browser è meno capace, faremo ricorso a jQuery per spostare l'elemento. Puoi utilizzare il plug-in jQuery Transform polyfill di Louis-Remi Babe per automatizzare tutto.

window.requestAnimationFrame

requestAnimationFrame è stato introdotto da Mozilla e migliorato da WebKit con l'obiettivo di fornire un'API nativa per l'esecuzione di animazioni, che si tratti di animazioni basate su DOM/CSS o su <canvas> o WebGL. Il browser può ottimizzare le animazioni simultanee in un unico ciclo di reflow e ridipintura, ottenendo un'animazione con una fedeltà superiore. Ad esempio, animazioni basate su JS sincronizzate con transizioni CSS o SVG SMIL. Inoltre, se esegui il loop di animazione in una scheda non visibile, il browser non lo manterrà in esecuzione, il che significa un minore utilizzo di CPU, GPU e memoria, con una durata della batteria molto più lunga.

Per ulteriori dettagli su come e perché utilizzare requestAnimationFrame, consulta l'articolo di Paul Irish requestAnimationFrame per animazioni intelligenti.

Profilazione

Quando scopri che la velocità della tua applicazione può essere migliorata, è il momento di approfondire la profilazione per scoprire dove le ottimizzazioni potrebbero generare il maggiore vantaggio. Le ottimizzazioni avranno spesso un impatto negativo sulla manutenibilità del codice sorgente e, pertanto, devono essere applicate solo se necessario. Il profiling ti indica quali parti del codice potrebbero generare i maggiori vantaggi se il loro rendimento venisse migliorato.

Profilazione di JavaScript

I profiler JavaScript forniscono una panoramica del rendimento dell'applicazione a livello di funzione JavaScript misurando il tempo necessario per eseguire ogni singola funzione dall'inizio alla fine.

Il tempo di esecuzione lordo di una funzione è il tempo complessivo necessario per eseguirla dall'alto verso il basso. Il tempo di esecuzione netto è il tempo di esecuzione lordo meno il tempo impiegato per eseguire le funzioni chiamate dalla funzione.

Alcune funzioni vengono chiamate più spesso di altre. I profiler in genere forniscono il tempo necessario per l'esecuzione di tutte le invocazioni, nonché il tempo di esecuzione medio, minimo e massimo.

Per maggiori dettagli, consulta la documentazione di Chrome DevTools sul profiling.

Il DOM

Le prestazioni di JavaScript influiscono notevolmente sulla fluidità e sulla reattività dell'applicazione. È importante capire che, sebbene i profiler JavaScript misurino il tempo di esecuzione del codice JavaScript, misurano indirettamente anche il tempo impiegato per le operazioni DOM. Queste operazioni DOM sono spesso alla base dei problemi di rendimento.

function drawArray(array) {
  for(var i = 0; i < array.length; i++) {
    document.getElementById('test').innerHTML += array[i]; // No good :(
  }
}

Ad esempio, nel codice riportato sopra non viene impiegato quasi nessun tempo per l'esecuzione del codice JavaScript effettivo. È comunque molto probabile che la funzione drawArray venga visualizzata nei tuoi profili perché interagisce con il DOM in modo molto inefficace.

Suggerimenti utili

Funzioni anonime

Le funzioni anonime non sono facili da profilare perché non hanno un nome con cui potrebbero essere visualizzate nel profiler. Esistono due modi per ovviare al problema:

$('.stuff').each(function() { ... });

riscrivi a:

$('.stuff').each(function workOnStuff() { ... });

Non è noto che JavaScript supporta le espressioni di funzioni con nome. In questo modo, verranno visualizzati perfettamente nel profiler. Questa soluzione presenta un problema: l'espressione con nome inserisce effettivamente il nome della funzione nell'ambito lessicale corrente. Fai attenzione, perché potresti sovrascrivere altri simboli.

Profilazione di funzioni lunghe

Immagina di avere una funzione lunga e sospetti che una piccola parte possa essere la causa dei problemi di prestazioni. Esistono due modi per scoprire quale parte presenta il problema:

  1. Metodo corretto: ristruttura il codice in modo da non includere funzioni lunghe.
  2. Il metodo malefico per fare le cose: aggiungi al codice istruzioni sotto forma di funzioni con nome che si richiamano a vicenda. Se fai un po' di attenzione, questo non cambia la semantica e fa sì che parti della funzione vengano visualizzate come singole funzioni nel profiler: js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... } Non dimenticare di rimuovere queste funzioni aggiuntive al termine del profiling o di utilizzarle come punto di partenza per il refactoring del codice.

Profilazione DOM

Gli ultimi strumenti per sviluppatori di Chrome Web Inspector contengono la nuova "Visualizzazione della sequenza temporale", che mostra una sequenza temporale delle azioni di basso livello eseguite dal browser. Puoi utilizzare queste informazioni per ottimizzare le operazioni DOM. Devi cercare di ridurre il numero di "azioni" che il browser deve eseguire durante l'esecuzione del codice.

La visualizzazione della cronologia può creare una quantità immensa di informazioni. Dovresti quindi provare a creare casi di test minimi che puoi eseguire in modo indipendente.

Profilazione DOM

L'immagine sopra mostra l'output della visualizzazione della sequenza temporale per uno script molto semplice. Il riquadro a sinistra mostra le operazioni eseguite dal browser in ordine cronologico, mentre la cronologia nel riquadro a destra mostra il tempo effettivo impiegato da una singola operazione.

Scopri di più sulla visualizzazione della cronologia. Uno strumento alternativo per il profiling in Internet Explorer è DynaTrace Ajax Edition.

Strategie di profilazione

Evidenziare aspetti

Quando vuoi eseguire il profiling della tua applicazione, cerca di individuare gli aspetti della sua funzionalità che potrebbero causare lentezza il più vicino possibile. Dopodiché, prova a eseguire un'esecuzione del profilo che esegua solo le parti di codice pertinenti a questi aspetti della tua applicazione. In questo modo, i dati di profilazione saranno più facili da interpretare perché non sono mescolati a percorsi di codice non correlati al problema effettivo. Alcuni esempi di aspetti specifici della tua applicazione potrebbero essere:

  1. Tempo di avvio (attiva il profiler, ricarica l'applicazione, attendi il completamento dell'inizializzazione, interrompi il profiler.
  2. Fai clic su un pulsante e sull'animazione successiva (avvia il profiler, fai clic sul pulsante, attendi il completamento dell'animazione, interrompi il profiler).
Profilazione della GUI

Eseguire solo la parte giusta dell'applicazione può essere più difficile in un programma GUI rispetto all'ottimizzazione, ad esempio, del ray tracer del tuo motore 3D. Ad esempio, se vuoi creare un profilo delle azioni che si verificano quando fai clic su un pulsante, potresti attivare eventi mouseover non correlati che rendono i risultati meno definitivi. Cerca di evitarlo :)

Interfaccia programmatica

Esiste anche un'interfaccia programmatica per attivare il debugger. In questo modo, puoi controllare con precisione quando inizia e termina il profilo.

Avvia una profilazione con:

console.profile()

Interrompi la profilazione con:

console.profileEnd()

Ripetibilità

Quando esegui la profilazione, assicurati di poter riprodurre i risultati. Solo allora potrai capire se le ottimizzazioni hanno effettivamente migliorato la situazione. Inoltre, il profiling a livello di funzione viene eseguito nel contesto dell'intero computer. Non si tratta di una scienza esatta. Le singole esecuzioni del profilo potrebbero essere influenzate da molti altri fattori che si verificano sul computer:

  1. Un timer non correlato nella tua applicazione che si attiva mentre misuri qualcos'altro.
  2. Il garbage collector al lavoro.
  3. Un'altra scheda del browser che richiede molto lavoro nello stesso thread di esecuzione.
  4. Un altro programma sul computer sta utilizzando la CPU, rallentando così l'applicazione.
  5. Cambiamenti improvvisi nel campo gravitazionale della Terra.

Inoltre, ha senso eseguire lo stesso percorso di codice più volte in una sessione di profilatura. In questo modo riduci l'influenza dei fattori sopra indicati e le parti lente possono risaltare ancora più chiaramente.

Misura, migliora, misura

Quando hai identificato un punto lento nel tuo programma, prova a pensare a come migliorare il comportamento di esecuzione. Dopo aver modificato il codice, esegui nuovamente il profilo. Se il risultato ti soddisfa, vai avanti. Se non noti un miglioramento, ti consigliamo di annullare la modifica e di non lasciarla attiva "perché non può far male".

Strategie di ottimizzazione

Riduci al minimo l'interazione con il DOM

Un tema comune per migliorare la velocità delle applicazioni client web è ridurre al minimo l'interazione con il DOM. Sebbene la velocità dei motori JavaScript sia aumentata di un ordine di grandezza, l'accesso al DOM non è aumentato allo stesso ritmo. Per motivi molto pratici, questo non accadrà mai (le operazioni come il layout e il disegno su uno schermo richiedono tempo).

Memorizza nella cache i nodi DOM

Ogni volta che recuperi un nodo o un elenco di nodi dal DOM, prova a pensare se potresti riutilizzarli in un calcolo successivo (o anche solo nell'iterazione successiva del ciclo). Spesso è così, a meno che non aggiungi o elimini effettivamente i nodi nell'area pertinente.

Prima:

function getElements() {
  return $('.my-class');
}

Dopo:

var cachedElements;
function getElements() {
  if (cachedElements) {
    return cachedElements;
  }
  cachedElements = $('.my-class');
  return cachedElements;
}

Memorizza nella cache i valori degli attributi

Allo stesso modo in cui puoi memorizzare nella cache i nodi DOM, puoi anche memorizzare nella cache i valori degli attributi. Immagina di animare un attributo dello stile di un nodo. Se sai che sei l'unico (come in quella parte di codice) che toccherà quell'attributo, puoi memorizzare nella cache l'ultimo valore in ogni iterazione in modo da non doverlo leggere ripetutamente.

Prima:

setInterval(function() {
  var ele = $('#element');
  var left = parseInt(ele.css('left'), 10);
  ele.css('left', (left + 5) + 'px');
}, 1000 / 30);

Dopo: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);

Spostare la manipolazione del DOM fuori dai loop

I loop sono spesso punti critici per l'ottimizzazione. Prova a trovare il modo di disaccoppiare l'elaborazione dei numeri effettivi dal lavoro con il DOM. Spesso è possibile eseguire un calcolo e, al termine, applicare tutti i risultati contemporaneamente.

Prima:

document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  document.getElementById('target').innerHTML += val;
}

Dopo:

var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
  var val = doSomething(array[i]);
  stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');

Ristrutturazioni e riflussi

Come discusso in precedenza, l'accesso al DOM è relativamente lento. Diventa molto lento quando il codice legge un valore che deve essere ricalcolato perché il codice ha modificato di recente qualcosa di correlato nel DOM. Pertanto, è consigliabile evitare di combinare l'accesso in lettura e scrittura al DOM. Idealmente, il codice dovrebbe sempre essere raggruppato in due fasi:

  • Fase 1: leggi i valori DOM necessari per il codice
  • Fase 2: modifica il DOM

Cerca di non programmare un pattern come:

  • Fase 1: leggi i valori DOM
  • Fase 2: modifica il DOM
  • Fase 3: leggi di più
  • Fase 4: modifica il DOM altrove.

Prima:

function paintSlow() {
  var left1 = $('#thing1').css('left');
  $('#otherThing1').css('left', left);
  var left2 = $('#thing2').css('left');
  $('#otherThing2').css('left', left);
}

Dopo:

function paintFast() {
  var left1 = $('#thing1').css('left');
  var left2 = $('#thing2').css('left');
  $('#otherThing1').css('left', left);
  $('#otherThing2').css('left', left);
}

Questo consiglio deve essere preso in considerazione per le azioni che si verificano all'interno di un contesto di esecuzione JavaScript. ad esempio all'interno di un gestore di eventi, di un gestore di intervalli o durante la gestione di una risposta ajax.

L'esecuzione della funzione paintSlow() riportata sopra crea questa immagine:

paintSlow()

Il passaggio all'implementazione più rapida genera questa immagine:

Implementazione più rapida

Queste immagini mostrano che riordinare il modo in cui il codice accede al DOM può migliorare notevolmente il rendimento del rendering. In questo caso, il codice originale deve ricalcolare gli stili e il layout della pagina due volte per creare lo stesso risultato. Un'ottimizzazione simile può essere applicata praticamente a tutto il codice "reale" e produrre risultati davvero significativi.

Scopri di più: Rendering: repaint, reflow/relayout, restyle di Stoyan Stefanov

Rielaborazioni e loop di eventi

L'esecuzione di JavaScript nel browser segue un modello "Event Loop". Per impostazione predefinita, il browser è in stato "inattivo". Questo stato può essere interrotto da eventi derivanti da interazioni degli utenti o da elementi come timer JavaScript o callback Ajax. Ogni volta che un codice JavaScript viene eseguito in un punto di interruzione, in genere il browser attende il termine dell'esecuzione fino a quando non viene ridipinto lo schermo (potrebbero esserci eccezioni per i JavaScript con tempi di esecuzione estremamente lunghi o in casi come le finestre di avviso che interrompono efficacemente l'esecuzione di JavaScript).

Conseguenze

  1. Se l'esecuzione dei cicli di animazione JavaScript richiede più di 1/30 di secondo, non potrai creare animazioni fluide perché il browser non verrà ridisegnato durante l'esecuzione di JS. Se prevedi di gestire anche gli eventi utente, devi essere molto più veloce.
  2. A volte è utile ritardare alcune azioni JavaScript fino a un momento successivo. Ad esempio setTimeout(function() { ... }, 0) In questo modo, viene chiesto al browser di eseguire il callback non appena il loop di eventi è di nuovo inattivo (in pratica, alcuni browser aspettano almeno 10 ms). Tieni presente che in questo modo verranno creati due cicli di esecuzione di JavaScript molto ravvicinati nel tempo. Entrambi potrebbero attivare una nuova pittura dello schermo, il che potrebbe raddoppiare il tempo complessivo impiegato per la pittura. L'attivazione o meno di due pitture dipende dalle strategie di ricerca nel browser.

Versione normale:

function paintFast() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  $('#otherThing2').css('height', '20px');
}
Rielaborazioni e loop di eventi

Aggiungiamo un po' di ritardo:

function paintALittleLater() {
  var height1 = $('#thing1').css('height');
  var height2 = $('#thing2').css('height');
  $('#otherThing1').css('height', '20px');
  setTimeout(function() {
    $('#otherThing2').css('height', '20px');
  }, 10)
}
Ritardo

La versione ritardata mostra che il browser esegue la pittura due volte, anche se le due modifiche alla pagina sono solo di 1/100 di secondo ciascuna.

Inizializzazione lazy

Gli utenti vogliono app web che si carichino rapidamente e che siano reattive. Tuttavia, gli utenti hanno soglie diverse per ciò che percepiscono come lento a seconda dell'azione che eseguono. Ad esempio, un'app non deve mai eseguire molti calcoli su un evento mouseover perché ciò potrebbe creare un'esperienza utente negativa mentre l'utente continua a muovere il mouse. Tuttavia, gli utenti sono abituati ad accettare un piccolo ritardo dopo aver fatto clic su un pulsante.

Pertanto, potrebbe essere opportuno spostare il codice di inizializzazione in modo che venga eseguito il più tardi possibile (ad esempio quando l'utente fa clic su un pulsante che attiva un determinato componente dell'applicazione).

Prima: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });

Dopo: js $('#button').click(function() { $('.ele > .other * div.className').show() });

Delegazione di eventi

Distribuire gli handler di eventi in una pagina potrebbe richiedere un tempo relativamente lungo e può anche essere noioso una volta che gli elementi vengono sostituiti dinamicamente, il che richiede di ricollegare gli handler di eventi ai nuovi elementi.

In questo caso, la soluzione consiste nell'utilizzare una tecnica chiamata delega di eventi. Anziché associare singoli gestori eventi agli elementi, la natura di bubbling di molti eventi del browser viene utilizzata associando il gestore evento a un nodo principale e controllando il nodo target dell'evento per verificare se è di interesse.

In jQuery, questo può essere facilmente espresso come:

$('#parentNode').delegate('.button', 'click', function() { ... });

Quando non utilizzare la delega di eventi

A volte può verificarsi il contrario: stai utilizzando la delega di eventi e hai un problema di prestazioni. In sostanza, la delega degli eventi consente un tempo di inizializzazione con complessità costante. Tuttavia, il prezzo del controllo se un evento è di interesse deve essere pagato per ogni chiamata dell'evento. Questo potrebbe essere costoso, soprattutto per gli eventi che si verificano di frequente, come "mouseover" o anche "mousemove".

Problemi e soluzioni comuni

Le cose che faccio in $(document).ready richiedono molto tempo

Consiglio personale di Malte: non fare mai nulla in $(document).ready. Prova a inviare il documento nella sua forma finale. OK, puoi registrare gli ascoltatori di eventi, ma solo utilizzando l'attributo id-selector e/o la delega di eventi. Per gli eventi costosi come "mousemove", ritarda la registrazione finché non sono necessari (evento mouseover sull'elemento pertinente).

Se invece devi fare qualcosa, ad esempio inviare una richiesta Ajax per ottenere dati effettivi, mostra una bella animazione. Potresti includere l'animazione come URI dati se si tratta di una GIF animata o di un'altra animazione simile.

Da quando ho aggiunto un filmato in Flash alla pagina, tutto è molto lento

L'aggiunta di Flash a una pagina rallenta sempre un po' il rendering perché il layout finale della finestra deve essere "negoziato" tra il browser e il plug-in Flash. Se non puoi evitare del tutto di inserire Flash nelle tue pagine, assicurati di impostare il parametro Flash "wmode" sul valore "window" (che è il valore predefinito). In questo modo, viene disattivata la possibilità di comporre elementi HTML e Flash (non potrai vedere un elemento HTML sopra il filmato Flash e il filmato Flash non può essere trasparente). Potrebbe essere un inconveniente, ma migliorerà notevolmente il rendimento. Ad esempio, guarda come youtube.com evita accuratamente di posizionare livelli sopra il player del film principale.

Sto salvando elementi in localStorage, ora la mia applicazione ha dei problemi

La scrittura in localStorage è un'operazione sincrona che comporta l'avvio del disco rigido. Non eseguire mai operazioni sincrone "a lungo termine" durante le animazioni. Sposta l'accesso a localStorage in un punto del codice in cui sai con certezza che l'utente è inattivo e non sono in corso animazioni.

Il profiling indica che un selettore jQuery è molto lento

Innanzitutto, assicurati che il selettore possa essere eseguito tramite document.querySelectorAll. Puoi testarlo nella console JavaScript. Se esiste un'eccezione, riscrivi il selettore in modo da non utilizzare estensioni speciali del framework JavaScript. In questo modo, il selettore nei browser moderni verrà accelerato di un ordine di grandezza.

Se il problema persiste o se vuoi ottenere velocità anche nei browser moderni, segui queste linee guida:

  • Fornisci informazioni il più specifiche possibile sul lato destro del selettore.
  • Utilizza un nome di tag che non usi spesso come parte del selettore più a destra.
  • Se non funziona, prova a riscrivere il codice in modo da poter utilizzare un selettore ID

Tutte queste manipolazioni del DOM richiedono molto tempo

Un insieme di inserimenti, rimozioni e aggiornamenti di nodi DOM può essere molto lento. In genere, questo problema può essere ottimizzato generando una stringa HTML di grandi dimensioni e utilizzando domNode.innerHTML = newHTML per sostituire i vecchi contenuti. Tieni presente che questo potrebbe essere molto dannoso per la manutenibilità e potrebbe creare link di memoria in IE, quindi fai attenzione.

Un altro problema comune è che il codice di inizializzazione potrebbe creare molto codice HTML. Ad esempio, un plug-in jQuery che trasforma una casella di selezione in una serie di div perché è ciò che volevano i designer, ignorando le best practice UX. Se vuoi davvero che la tua pagina sia veloce, non farlo mai. Carica invece tutto il markup lato server nella sua forma finale. Anche questa opzione presenta molti problemi, quindi valuta attentamente se la velocità vale il compromesso.

Strumenti

  1. JSPerf - Esegui il benchmark di piccoli snippet di JavaScript
  2. Firebug: per il profiling in Firefox
  3. Strumenti per sviluppatori di Google Chrome (disponibile come WebInspector in Safari)
  4. DOM Monster - Per ottimizzare le prestazioni del DOM
  5. DynaTrace Ajax Edition: per il profiling e le ottimizzazioni di pittura in Internet Explorer

Per approfondire

  1. Google Speed
  2. Paul Irish sulle prestazioni di jQuery
  3. Rendimento JavaScript estremo (presentazione)