Optimalizácia JavaScript a jQuery kódu

17. února 2011

Po zadaní tohto termínu do Googlu na vás vybafne nemalé množstvo rôznych rád a tipov. Každá rada ale má svoju špecifickú hodnotu.

Jedna je použiteľná len pre určitý scenár, zatiaľ čo iná by sa dala považovať za základ optimalizácie. Cieľom tohto článku je podeliť sa s vami o základné princípy optimalizácie pri používaní JavaScriptu a jQuery knižnice, ktoré som vypozoroval dlhodobou praxou.

Nadmerné kalkulácie v cykloch

for (var i = 0; i < (b * c); i++) {
	// ... akykolvek kod
}

Pri používaní for a while cyklov sa občas stane, že v rýchlosti (napr. pri prvotnom testovaní kódu) v nich zabudneme nejakú kalkuláciu. A tak namiesto jednej kalkulácie, ktorej výsledok sa použije opakovane, ju robíme stále dokola.

V našom prípade sa pri každom cykle musí spočítať kalkulácia (b * c), ktorej výsledná hodnota je však konštantná. Táto kalkulácia môže byť teda zapísaná ako:

var calc = (b * c);
for (var i = 0; i < calc; i++) {
	// ... akykolvek kod
}

innerHTML nie je vždy najbezpečnejšia varianta

// nativne innerHTML
document.getElementById('moj_DIV').innerHTML = 'Text alebo HTML';
// jQuery .html() funkcia
$('#moj_DIV').html('Text alebo HTML');

JavaScriptové knižnice ako napr. jQuery vznikli, aby programátorom zjednodušili život nielen zrýchlením práce s DOM elementmi, ale aj v súvislosti s bezpečnosťou aplikácie. Čítal som mnoho komentárov, kde vývojári argumentujú rýchlosťou použitia funkcie innerHTML na zmenu obsahu elementu namiesto použitia .html() funkcie z jQuery.

Táto vlastnosť sa funkcii innerHTML určite poprieť nedá. No ak sa rozhodnete pre jej priame použitie, odporúčam prečítať si aspoň začiatok kódu funkcie html() z jQuery. Spomínajú sa tam napríklad pretečenia pamäte a iné zaujímavé veci…

Pri dlhých textoch je rýchlejšie použiť pole

var tabulka = '<table>';
var riadky = new Array();
for (var i = 0; i <= 1000; i++) {
	riadky.push('<tr><td>TEXT</td></tr>');
}
tabulka += riadky.join(„\n“) + '</table>';

Ak máme extra dlhý text (napr. HTML tabuľky s 1000 riadkami) a skladáme ho po častiach, je rýchlejšie ak použijeme pole, do ktorého pridávame postupne všetky položky a nakoniec použijeme funkciu join() na ich spojenie.

Čo najmenej modifikácií DOM štruktúry

JavaScript ako taký je vo svojich funkciách pomerne rýchly. Aj zložité matematické operácie vykonáva silou vašeho procesoru, čo v dnešných dňoch znamená takmer okamžité výsledky. Sú to práve dynamické zmeny dokumentu a hľadanie elementov na stránke, ktoré pri nesprávnom použití spomaľujú prehliadače – a to niekedy nad úroveň znesiteľnosti užívateľa.

Ak potrebujete urobiť hromadné zmeny v DOM štruktúre (napr. pridať slovo „koniec“ do textu každého stĺpca v tabuľke), určite to nerobte takto:

$('#tabulka td').each(function() {
	$(this).text($(this).text() + 'koniec');
});

Takýto postup donúti prehliadač po každej zmene textu v každom stĺpci obnoviť svoju vnútornú štruktúru DOM dokumentu a tiež to, čo sa objavuje na stránke. Samozrejme, že majú prehliadače určité medze a robia takéto zmeny v dávkach, takže stránka následne pri zmene viac riadkov nebliká. Ale predstavte si, že máte takto zmeniť 200 riadkov naraz… alebo 2000. Výsledok bude pravdepodobne niekoľko sekúnd čakania. Môžete to však skúsiť napríklad takto:

// vytvorime kopiu tela tabulky a zmeny vykoname na nej
// DOM sa zmeni az nakoniec, takze takyto postup nezatazi prehliadac
var t = $('#tabulka tbody').clone();
t.find('td').each(function() {
	var e = $(this);
	e.text(e.text() + 'koniec');
});

// nahradme cele upravene telo tabulky v jednom kroku
$('#tabulka tbody').replaceWith(t);

Duplicitné vytváranie jQuery objektov

$(this).text($(this).text() + 'koniec');

V predošlom tipe som v prvom prípade schválne 2x použil funkciu $(this), teda funkciu ktorá premení aktuálny HTML element na jQuery objekt. Takéto použitie však musí zakaždým vytvoriť nový jQuery objekt z toho istého elementu, a tak zbytočne pridáva čas a pamäť potrebné na takúto konverziu. Je omnoho úspornejšie uložiť taký objekt do premennej a v následnom kóde ho použiť takto:

var e = $(this);
e.text(e.text() + 'koniec');

Vyhľadávanie DOM elementov

Ako každý správny začiatočník, aj ja sám som používal označovanie DOM elementov triedami namiesto ID a naviac sa ich následne snažil hľadať selektormi ako a.new_window. Poďme sa teda spoločne pozrieť na pár zásad, ktorých správnosť som pomocou profilovania kódu odpozoroval:

  • Ak sa dá, použite ID elementu.
    • toto je najrýchlejšia cesta ako jQuery dokáže vyhľadať element na stránke, keďže používa natívnu funkciu getElementById()
    • pozor, aj keď sa to môže zdať ako hlúposť, v Internet Exploreri je rozdiel medzi: $('#odkaz') a $('a#odkaz'). Explorer v druhom prípade najskôr vyhľadá všetky odkazy (tagy a) a až potom z nich vyberie ten, ktorý má ID odkaz. Je to divné, ale je to tak. Používajte na začiatku teda vždy krížik.
  • Druhý najrýchlejší selektor sa bohužiaľ už líši od prehliadača… je ním buď:
    • $('a') – kde „a“ môže byť akýkoľvek názov elementu. Tento selektor používa natívnu funkciu getElementsByTagName(). Toto je platné pre všetky prehliadače, až na Firefox 3+
    • $('a.trieda') – vyhľadávanie podľa triedy. Z testov iných kóderov vychádza, že je táto metóda vo Firefoxe 3 a vyššom rýchlejšia než predošlá.
  • Čo najpresnejšie selektory
    • ak nie je dostupné ID elementu, odporúčam používať čo najpresnejšiu cestu k elementu (napr. "div.obsah table.tabulka td a.special")
    • v prípade, že by sme tu použili len „a.special“, jQuery bude musieť prejsť všetky odkazy na stránke a zistiť, či vyhovujú našej triede „.special“
    • ak ale použijeme presnú cestu k tabuľke, kde sú takéto odkazy umiestnené, bude jQuery hľadať triedu „.special“ už výhradne v uvedenej tabuľke a ušetrí nám (hlavne v Exploreri 6) kopec času a výpočetného výkonu

Delegácia udalostí (tzv. bubbling)

Tento tip je síce na konci, ale nie je o nič menej dôležitý ako tipy pred ním. Možno naopak – patrí medzi najdôležitejšie. Na webe neexistuje viditeľný tretí rozmer, avšak vďaka štruktúre HTML je možné položiť jeden element na druhý (resp. do druhého). Keď sa teda na stránke klikne na nejaký element, tento klik sa prenáša od elementu, ktorý je úplne v spodnej časti (napr. TD v tabuľke) cez jeho rodičov (TR, TBODY, TABLE, BODY) až po document a window objekty.

Pokiaľ má TD v tabuľke priradenú onclick udalosť, je táto obslúžená a pokračuje sa ďalej. Ak má TR priradenú onclick udalosť, je následne obslúžená aj táto. A tak ďalej, až po samotný document objekt. Toto môžeme veľmi dobre využiť ak máme napríklad odkazy v tabuľke, po kliknutí na ktoré potrebujeme vyvolať akciu špecifickú pre každý z nich.

Namiesto priraďovania onclick udalosti každému z nich:

$('#tabulka td a').click(function() {
	alert('Klikol si na odkaz s textom: '+$(this).text());
});

… je možné vďaka delegácii urobiť obsluhu nasledovne:

$('#tabulka').click(function(e) {
	var target = e.target; // e.target je element, na ktory sa kliklo
	if (target.nodeName == 'A') {
		alert('Klikol si na odkaz s textom: '+$(target).text());
	}
});

Výhody – namiesto vytvorení 50 onclick udalostí pre jednotlivé odkazy sa vytvorí jedna onclick udalosť pre tabuľku, ktorá obsahuje všetky naše odkazy. Tento postup výrazne šetrí čas a výpočetný výkon. Možno aj preto boli preň vytvorené vlastné funkcie priamo v jQuery, a to .live() a .delegate(). O rozdieloch medzi nimi a ich použití si povieme však v inom článku.

Pár rýchlych tipov na koniec

  • Naučte sa používať Profilovanie vo Firebugu pre Firefox (je to výborný nástroj na meranie času potrebného na vykonanie skriptov na stránke – a to pred aj po optimalizácii)
  • Minifikujte svoj kód – a najlepšie rovno zlúčte všetky JavaScript súbory, ktoré sú používané na každej stránke do jedného, a ten potom minifikujte
  • Nepoužívajte inline JavaScript (napr. onclick="alert('ahoj!')") – všetko JS kódovani by malo byť umiestnené v externom súbore, ktorý býva následne vložený buď do hlavičky alebo na koniec kódu stránky
  • Šetrite miestom – namiesto používania kľúčového slova var pre každú premennú sa dajú viaceré premenné zapísať aj takto: var abc = 'nieco', def = 12, ghi = new Array();

(Pozor na použitie syntaxe abc = def = 'nieco‚ – premenná def bude v tomto prípade vždy obsahovať to isté, čo obsahuje aj abc, a to aj keď sa ich budete snažiť nezávisle zmeniť.)

Zdroje:

Mohlo by vás také zajímat

Nejnovější

3 komentářů

  1. Peter

    Úno 17, 2011 v 18:37

    dobry clanok, prave pracujem na malom projekte, kde sa vyuziva aj jquery a taketo rady sa mi celkom hodia

    Odpovědět
  2. SiseL

    Úno 19, 2011 v 19:25

    chvalim autora za clanok. vdaka. mam interval v citacke, tak ak bude nieco dalsie, pockam.

    Odpovědět
  3. Martin Ambruš

    Úno 19, 2011 v 20:41

    vdaka za chvalu… dalsi clanok bude jQuery a delegacia udalosti (event delegation), takze zostante naladeni :-)

    Odpovědět

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *