dom 25 giugno 2017 - Lo Sviluppatore  anno III

JQuery: 10 regole per migliorare le performace

Condividi

Le applicazioni web contemporanee sono sempre più sofisticate, con interfacce grafiche accantivanti e funzionalità complesse. Normalmente quando si costruisce un’applicazione di questo tipo è auspicabile che l’applicazione abbia dei tempi di risposta all’utente adeguati e che appaiano “scattanti” anzichè “mosce”  E’ quindi anche il caso delle applicazioni web che fanno uso di jQuery per la manipolazione del DOM. Questo tipo di operazioni  senza l’utilizzo di alcuni accorgimenti, appesantiscono molto il lavoro che deve fare il browser e di conseguenza anche la CPU e la memoria.

Di seguito un elenco di best practice relative alle operazioni di base di JQuery, orientate a migliorare le prestazioni dei nostri programmi lato client.

  1. Usare i giusti selettori
  2. Usare JQuery solo quando necessario
  3. Riutilizzare gli oggetti JQuery (cache)
  4. Sfruttare la potenza della concatenazione dei metodi
  5. Utilizzare le sub-query
  6. Limitare la manipolazione diretta del DOM
  7. Leverage Event Delegation (a.k.a. Bubbling)
  8. Eliminare le query “spazzatura”
  9. Usare $(window).load
  10. Comprimere i file JS

 

1. Usare i giusti selettori

Il selettore più veloce in jQuery è il selettore per ID ( $('#unid') ). Questo perché è mappato direttamente ad un metodo JavaScript nativo, getElementById() .

Consideriamo per tutti gli esempi che seguirannno il seguente frammento HTML:

<div id="content">
	<form method="post" action="/">
		<h2>Semaforo</h2>
		<ul id="semaforo">
			<li><input type="radio" class="on"  name="light" value="rosso" /> Red</li>
			<li><input type="radio" class="off" name="light" value="giallo" /> Yellow</li>
			<li><input type="radio" class="off" name="light" value="verde" /> Green</li>
		</ul>
		<input class="button" id="bottone_vai" type="submit" value="Vai" />
	</form>
</div>

Selezione di un singolo elemento

Selezione dell’elemento button della form :

più lento

var bottone = $('#content .button');

più veloce

var bottone = $('#bottone_vai');

Selezione di più elementi

Questo il caso in cui si parla davvero di DOM traversal e looping, operazioni che risultano essere di un certo peso per il browser. Per questo tipo operazioni è sempre preferibile, per ridurre al minimo il calo di prestazioni, discendere sempre dal’ID del genitore più vicino :

Selezione dei campi di input :

più lento

var bottone = $('input');

più veloce

var lights = $('#semaforo input');

Il secondo selettore più veloce di JQuery, dopo quello per id è il selettore per Tag, anche questo mappato direttamente in un metodo nativo JavaScript getElementsByTagName() ed è quello appena visto nell’esempio sopra nella versione più e meno performante.

Subito dopo c’è il selettore per class (visto anche nel primo esempio) anche esso basato su un metodo nativo, getElementsByClassName()

Esempio selezione del campo di input di classe on :

var luce_attiva = $('#semaforo input.on');

Nota: Il selettore di classe è tra i selettori più lenti in jQuery, in IE si scorre tutto il DOM. Evitarne l’uso quando possibile. Mai usare un nome di tag come prefisso di una selezione per id. Per esempio, questo è lento perché ciclerà tra tutti i <div> del documento alla ricerca di quello il cui id=”contenuto” :

var content = $('div#contenuto');

2. Usare JQuery solo quando necessario

In questo articolo questa regola potebbe sembrare “fuori luogo” e invece no, perchè per certi tipi di operazioni è preferibile usare il Javascript nativo anzichè JQuery. Vediamo alcuni casi

“Abuso ” dell’utilizzo del metodo attr()

più lento

$('a').bind(‘click’, function(){
  console.log('Hai cliccato: ' + $(this).attr('id'));
});

più veloce

$('a').bind(‘click’, function(){
 console.log('Hai cliccato: ' + this.id);
});

Usa il ciclo FOR anzichè EACH

più lento

$.each (array, function (i) {  
    array[i] = i;  
});

più veloce

var l = array.length;  
for (var i=0; i<l; i++) {  
    array[i] = i;  
}

3. Riutilizzare gli oggetti JQuery (cache)

E’ buona abitudine  salvare gli oggetti jQuery in una variabile (come anche visto negli esempi precedenti). Per esempio, mai fare questo:

più lento

$('#semaforo input.on).bind('click', function(){...});
$('#semaforo input.on).css('border', '3px dashed yellow');
$('#semaforo input.on).css('background-color', 'orange');
$('#semaforo input.on).fadeIn('slow');

Invece fare così:

più veloce

var $luce_attiva = $('#semaforo input.on');
$luce_attiva.bind('click', function(){...});
$luce_attiva.css('border', '3px dashed yellow');
$luce_attiva.css('background-color', 'orange');
$luce_attiva.fadeIn('slow');

In quest’ultimo modo effettuiamo la ricerca sul DOM solo una volta, lavorando sul riferimento all’oggetto jQuery. Nella maggior parte dei casi otterremo un aumento delle perfomance superiore al 50%.

4. Sfruttare la potenza della concatenazione dei metodi

Sfruttando il fatto che ogni metodo JQuery resitutisce sempre l’oggetto a cui è stata applicata, per l’esempio precedente potevamo scrivere:

var $luce_attiva = $('#semaforo input.on');
$luce_attiva.bind('click', function(){...})
	.css('border', '3px dashed yellow')
	.css('background-color', 'orange')
	.fadeIn('slow');

Questo ci permette di scrivere meno codice, rendendo il nostro JavaScript più leggero.

5. Utilizzare le Sub-queries

jQuery ci permette di eseguire le operazioni di selezione supplementari su un oggetto padre precedentemente selezionato è memorizzato in una variabile locale. Questo riduce il sovraccarico delle prestazioni nelle selezioni successive. Per esempio, possiamo sfruttare le sub-query per selezionare le luci attive e inattive e cacharle per successivi utilizzi:

var $semaforo = $('#semaforo'),
$luce_attiva = $semaforo.find('input.on'),
$luci_inattive = $semaforo.find('input.off');

6. Limitare la manipolazione diretta del DOM

L’idea di base è quella di creare esattamente quello che ci serve in memoria, e quindi aggiornare il DOM. Questa non è una delle migliori pratiche jQuery, ma è un must per la programmazione JavaScript efficiente. La manipolazione del DOM diretta è lenta. Per esempio, se avete bisogno di creare dinamicamente una lista di elementi, non fare questo:

più lento

var top_100_list = [...], // array di 100 stringhe
$mylist = $('#mylist'); // jQuery seleziona il nostro elemento <ul>

for (var i=0, l=top_100_list.length; i<l; i++)
{
	$mylist.append('<li>' + top_100_list[i] + '</li>');
}

più veloce

E’ meglio invece prima creare tutta la stringa e poi “attaccare” al DOM una sola volta:

var top_100_list = [...], // array di 100 stringhe
$mylist = $('#mylist'), // jQuery seleziona il nostro elemento <ul>
top_100_li = ""; // Questo servirà per memorizzare i list items <li>

for (var i=0, l=top_100_list.length; i<l; i++)
{
	top_100_li += '<li>' + top_100_list[i] + '</li>';
}
$mylist.html(top_100_li); // inseriamo nel DOM

ancora più veloce

Creare la stringa appendendo le liste di elementi ad un elemento padre e sostituire l’elemento nel DOM:

var top_100_list = [...]; // array di 100 stringhe
$mylist = $('#mylist'); // jQuery seleziona il nostro elemento <ul>
// Questo servirà per memorizzare l'intera struttura 
// della lista <ul>
top_100_ul = '<ul id="#mylist">'; 

for (var i=0, l=top_100_list.length; i<l; i++) { 
 top_100_ul += '<li>' + top_100_list[i] + '</li>'; 
} 
top_100_ul += '</ul>'; // Chiudiamo la nostra lista non ordinata 
$mylist.replaceWith(top_100_ul); // sostituiamo nel DOM

7. Leverage Event Delegation (a.k.a. Bubbling)

A meno che non si specifichi diversamente , ogni evento JavaScript  (es. click, mouseover, ecc) si propaga su nell’albero del DOM agli elementi genitore del nodo che l’ha triggerato, come le “bolle” appunto (da qui il termine “bubbling”). Questo è incredibilmente utile quando vogliamo che molti elementi (nodi) esegueno  la stessa funzione. Invece di associare una funzione di listener per ogni elemento che è un operazione inefficiente, è possibile associare una sola volta al loro genitore, riuscendo comunque a sapere quale nodo figlio ha attivato l’evento. Supponiamo di avere una form con molti input, ad esempio liste non ordinate molto lunghe, e vogliamo che quando si clicca un elemento a questo venga aggiunta una classe, ad esempio per cambiarne il colore. Un binding di questo tipo è inefficiente:

più lento

$('#myList li).bind('click', function(){
	$(this).addClass('clicked');
	// fai qualcosa
});

più veloce

Invece è meglio “mettersi in ascolto” sull’evento click sul nodo padre:

$('#myList).bind('click', function(e){
	var target = e.target, // e.target è il nodo che ha generato l'evento
	$target = $(target);  // wrappiamo il nodo in un oggetto jQuery
	if (target.nodeName === 'LI') {
		$target.addClass('clicked');
		// fai qualcosa
	}
});

Il nodo padre funge da dispatcher e può quindi fare il lavoro sulla base dell’elemento (nodo) da cui è provenuto l’evento. In generale se trovate binding di un listener di eventi a molti elementi, facilmente si sta facendo qualcosa di sbagliato (e lento).

8. Eliminare le query “spazzatura”

Se si dispone di un solo JavaScript globale per l’intero sito, si può essere tentati di buttare tutte le tue funzioni jQuery in $(document).ready(function () {/ / tutto il mio glorioso codice }) . Non è proprio una buona idea. Sebbene jQuery non generi alcun problema, se non trova qualche elemento riferito in una funzione ma non presente nel DOM,  ci vuole sempre del tempo per accorgersi che non ci sono. La soluzione è quella di eseguire solo le funzioni che sono applicabili alla pagina. Il modo più efficace per farlo è quello di utilizzare le funzioni di inizializzazione inline in modo che il modello ha il pieno controllo su quando e dove viene eseguito il codice JavaScript. Ad esempio, nella pagina “articolo”, è necessario includere il seguente codice prima della fine del body o comunqe subito dopo i campi su cui il codice Javascript agisce:

<script type="text/javascript>
 mylib.articolo.init ();
</script>
</body>

mentre il nostro file globale Javascript sarà un qualcosa di simile a questo:

var mylib =
{
	articolo:
	{
		init: function ()
		{
			/ /  funzioni jQuery specifiche per la pagina articolo
		}
	},
	semaforo:
	{
		init: function ()
		{
			/ /  funzioni jQuery specifiche per la pagina semaforo
		}
	}
}

9. Usare $(window).load

C’è una tentazione tra gli sviluppatori jQuery che è quella di collegare tutto in $(document). ready. Dopo tutto, è utilizzato nella maggior parte dei esempi che si trovano in giro. Anche se $ (document). ready è incredibilmente utile, questo viene chiamato durante il rendering della pagina mentre gli oggetti sono ancora in fase caricamento. Se notate a volte c’è una sorta di stallo durnte il caricamento di una pagina, questo potrebbe essere causato da troppe operazioni dentro i $(document).ready . È possibile ridurre l’utilizzo della CPU durante il caricamento della pagina, legando le funzioni jQuery all’evento $ (window). load
evento, che si verifica dopo che tutti gli oggetti chiamati dal codice HTML sono stati caricati in memoria.

$(window).load(function(){
	// funzioni jQuery da invocare dopo che la pagina è stata inizializzata.
});

Funzionalità superflue come drag and drop, effetti visivi e animazioni vincolanti, pre-fetching di immagini nascoste, ecc, sono tutti buoni candidati per l’uso di questa tecnica.

10. Comprimere i file JS

Anche se non strettamente legato a JQuery una cosa da fare per ottimizzare il codice JavaScript è quello di comprimere i file JS. Un esempio di tool per fare questo tipo di operazione è YUICompressor

Altro buon accorgimento da usare è quello di utilizzare sempre l’ultima versione di JQuery, avendo la cura di eseguire i test di regressione quando si fa l’aggiornamento. Le nuove versioni tipicamente sono migliorate in termini di performance.

Risorse utili:

Documentazione ufficiale di JQuery

jQuery Visual Cheat Sheet (PDF)

 

Lascia un commento

Top