Rozdział 9. Wydajność — najlepsze rozwiązania

17 października 2012
1 gwiadka2 gwiazdki3 gwiazdki4 gwiazdki5 gwiazdek

Część trzecia. Zagadnienia zaawansowane

Poniższa sekcja jest w budowie

Odwiedź stronę https://github.com/jquery/web-learn-jquery-com, aby pomóc nam ją współtworzyć!

Wydajność — najlepsze rozwiązania

W tym rozdziale omówionych zostanie kilka najlepszych rozwiązań dotyczących biblioteki jQuery i języka JavaScript. Kolejność poruszanych zagadnień jest bez znaczenia. Znaczna część rozdziału została napisana w oparciu o prezentację Paula Irisha jQuery Anti-Patterns for Performance.

Jeśli korzystasz z pętli, zapisuj długość w pamięci podręcznej

W przypadku pętli for nie należy pobierać długości tablicy przy każdej iteracji — lepiej wcześniej zapisać ją w pamięci podręcznej.

	var mojaDlugosc = mojaTablica.length;
	 
	for (var i = 0; i < mojaDlugosc; i++) {
	    // zrób coś
	}

Dodawaj nową treść poza pętlą

Naruszanie struktury DOM obciąża pamięć — jeśli więc chcesz dodać wiele elementów, zrób to za jednym podejściem.

	// tak nie powinno się robić
	$.each(mojaTablica, function(i, pozycja) {
	   var nowaPozycjaNaLiscie = '<li>' + pozycja + '</li>';
	   $('#koszykarze').append(nowaPozycjaNaLiscie);
	});
	 
	// lepiej zrobić to tak
	var frag = document. createDocumentFragment();
	 
	$.each(mojaTablica, function(i, pozycja) {
	    var nowaPozycjaNaLiscie = '<li>' + pozycja + '</li>';
	    frag. appendChild(nowaPozycjaNaLiscie);
	});
	$('#koszykarze')[0].appendChild(frag);
	 
	// bądź zrób to tak
	var mojHtml = '';
	 
	$.each(mojaTablica, function(i, pozycja) {
	    html += '<li>' + pozycja + '</li>';
	});
	$('#koszykarze').html(mojHtml);

Nie powtarzaj się

Stosuj się do powyższej zasady — jeśli się powtarzasz, to robisz coś źle.

	// ZŁY kod
	if ($zdarzeniefade.data('obecnie') != 'pokazuje') {
	    $zdarzeniefade.stop();
	}
	 
	if ($zdarzeniehover.data('obecnie') != 'pokazuje') {
	    $zdarzeniehover.stop();
	}
	 
	if ($spany.data('obecnie') != 'pokazuje') {
	    $spany.stop();
	}
	 
	// DOBRY kod!
	var $elementy = [$zdarzeniefade, $zdarzeniehover, $spany];
	$.each($elementy, function(i,elem) {
	    if (elem.data('obecnie') != 'pokazuje') {
	        elem.stop();
	    }
	});

Strzeż się funkcji anonimowych

Wszędobylskie funkcje anonimowe są bardzo uciążliwe. Trudno jest wyszukać w nich usterki, a także je przetestować czy ponownie wykorzystać. Kłopoty sprawia również ich odpowiednie utrzymanie. Zamiast nich do organizacji i nazywania procedur obsługi oraz wywołań zwrotnych skorzystaj z literału obiektowego.

	// ZŁY kod
	$(document).ready(function() {
	    $('#magia').click(function(z) {
	        $('#superefekty').slideUp(function() {
	            // ...
	        });
	    });
	 
	    $('#szczescie').load(url + ' #jednorozce', function() {
	        // ...
	    });
	});
	 
	// LEPIEJ
	var PI = {
	    gotowy : function() {
	        $('#magia').click(PI.cukierkowaGora);
	        $('#szczescie').load(PI.url + ' #jednorozce', PI.wzJednorozca);
	    },
	 
	    cukierkowaGora : function(z) {
	        $('#superefekty').slideUp(PI.wzPrzesuniecia);
	    },
	 
	    przesunWz : function() { ... },
	 
	    jednorozecWz : function() { ... }
	};
	 
	$(document).ready(PI.gotowy);

Zoptymalizuj selektory

Optymalizacja selektorów nie ma obecnie aż tak dużego znaczenia, ponieważ coraz więcej przeglądarek ma zaimplementowaną funkcję document.querySelectorAll() — oznacza to, że wybór elementów spoczywa na samej przeglądarce, a nie na bibliotece jQuery. Warto jednak zapamiętać kilka wskazówek.

Selektory oparte na identyfikatorach

Wybieranie elementów zawsze najlepiej zacząć od ich identyfikatora.

	// szybki sposób
	$('#kontener div.ramierobota');
	 
	// super szybki sposób
	$('#kontener').find('div.ramierobota');

Korzystanie z metody $.fn.find jest szybsze, ponieważ pierwszy wybór dokonywany jest bez wykorzystania silnika Sizzle. Wybieranie elementów wg selektorów opartych na identyfikatorze obsługiwane jest przez metodę document.getElementById(), która jest rodzimą metodą przeglądarki — stąd też sposób ten jest bardzo szybki.

Precyzyjność

Zadbaj o to, by prawa strona selektora była bardziej sprecyzowana, zaś lewa mniej.

	// niezoptymalizowany selektor
	$('div.data .gonzalez');
	 
	// zoptymalizowany
	$('.data td.gonzalez');

Jeśli to możliwe, po prawej stronie utwórz selektor wg wzoru znacznik.klasa, a po lewej uwzględnij tylko klasę.

Nie przesadzaj z dokładnością.

	$('.data table.attendees td.gonzalez');
	 
	// lepsze rozwiązanie: jeśli to możliwe, zrezygnuj ze środkowej części selektora
	$('.data td.gonzalez');

Bardziej „płaska” struktura DOM również poprawia szybkość znajdowania elementów, ponieważ liczba warstw, które muszą zostać przejrzane w poszukiwaniu wybranego elementu jest mniejsza.

Unikaj selektora uniwersalnego

Selektory określające lub wskazujące na to, że wybrane elementy można znaleźć w dowolnym miejscu są bardzo wolne.

	$('.przyciski > *');  // bardzo duży koszt pamięciowy
	$('.przyciski').children();  // o wiele lepiej
	 
	$('.gatunek :radio');  // w istocie jest to selektor uniwersalny, choć może się wydawać inaczej
	$('.gatunek *:radio'); // to samo, tylko teraz jest to oczywiste
	$('.gatunek input:radio'); // o wiele lepiej

Korzystaj z delegacji zdarzeń

Dzięki delegacji zdarzeń można powiązać procedurę obsługi z jednym kontenerem (np. z nieuporządkowaną listą) zamiast z wieloma elementami znajdującymi się w tym kontenerze (np. z pozycjami na liście). Zadanie ułatwiają dostępne w bibliotece jQuery metody $.fn.live i $.fn.delegate. Kiedy to tylko możliwe należy korzystać z metody $.fn.delegate, ponieważ dzięki niej unikniemy wybrania zbędnych elementów, a jej konkretny kontekst zmniejsza narzut o około 80% (por. z kontekstem dokumentu metody $.fn.live).

Delegacja zdarzeń nie tylko pozwala zwiększyć wydajność. Dzięki niej możemy również dodawać nowe elementy kontenera na stronę bez potrzeby ponownego wiązania ich z procedurami obsługi — te są już dodane.

	// źle (jeśli mamy do czynienia z długą listą)
	$('li.wyzwalacz').click(PrObslugi);
	 
	// lepiej skorzystać z delegacji zdarzeń za pomocą metody $.fn.live 
	$('li.wyzwalacz').live('click', PrObslugi);
	 
	// najlepiej zaś skorzystać z delegacji zdarzeń za pomocą metody $.fn.delegate 
	// można łatwo określić kontekst
	$('#mojaLista').delegate('li.wyzwalacz', 'click', PrObslugi);

Odłącz elementy, by rozpocząć na nich pracę

Model DOM jest wolny, dlatego najlepiej ograniczyć wykonywane na nim manipulacje do minimum. W wersji jQuery 1.4 wprowadzono metodę $.fn.detach, która pomaga uniknąć tego problemu — dzięki niej można usunąć ze struktury DOM element podczas pracy nad nim.

	var $tabela = $('#mojaTabela');
	var $rodzic = $tabela.parent();
	 
	$tabela.detach();
	// ...tutaj dodaj wiele rzędów tabeli
	$rodzic.append(tabela);

Do zmieniania stylów CSS wielu elementów skorzystaj z arkuszy stylów

Jeśli zmieniasz styl ponad 20 elementów, zamiast używać metody $.fn.css warto dodać do strony element style, co przyspieszy jej działanie o prawie 60 %.

	// dobry sposób dla maksymalnie 20 elementów, w przypadku większej ilości kod działa wolno
	$('a.swedberg').css('color', '#asd123');
	$('<style type="text/css">a.swedberg { color : #asd123 }</style>')
	    .appendTo('head');

Korzystaj z metody $.data zamiast $.fn.data

Wywołanie metody $.data na elemencie DOM zamiast metody $.fn.data na elementach wybranych przez jQuery może być do dziesięciu razy szybsze. Przed skorzystaniem z tego sposobu upewnij się jednak, że znasz różnicę pomiędzy elementem DOM a zestawem elementów jQuery.

	// tradycyjny sposób
	$(elem).data(klucz,wartość);


	 
	// 10 razy szybciej
	
$.data(elem,klucz,wartość);

Nie wykonuj operacji na niewybranych elementach

Jeśli spróbujesz wykonać obszerny blok kodu na pustym zbiorze elementów, jQuery nic nie zasygnalizuje — kod zostanie wykonany normalnie. Sam musisz sprawdzić, czy wybrany przez ciebie zestaw zawiera jakieś elementy.

	// ZŁY SPOSÓB: kod wykona trzy funkcje
	// zanim zorientuje się, że zbiór
	// jest pusty
	$('#brakczegostakiego').slideUp();
	 
	// Lepiej
	var $wybraneElementy = $('#brakczegostakiego');
	if ($wybraneElementy.length) { $wybraneElementy.slideUp(); }
	 
	// NAJLEPSZE rozwiązanie: dodaj wtyczkę wykonajRaz
	jQuery.fn.wykonajRaz = function(funk){
	    this.length && funk.apply(this);
	    return this;
	
}
	 
	
$('li.dodanedokoszyka').wykonajRaz(function(){

	    // dodaj Ajax! o/

	});

Rada ta dotyczy zwłaszcza widżetów jQuery UI, w przypadku których występują znaczne narzuty, jeśli nie uda nam się wybrać żadnych elementów.

Definiowanie zmiennych

Wszystkie zmienne można zdefiniować za pomocą zaledwie jednej instrukcji.

	// stary i sfatygowany sposób
	var test = 1;
	var test2 = function() { ... };
	var test3 = test2(test);
	 
	// nowe cacko
	var test = 1,
	    test2 = function() { ... },
	    test3 = test2(test);

W przypadku samowykonujących się funkcji, definicja zmiennej może zostać całkowicie pominięta.

	(function(foo, bar) { ... })(1, 2);

Instrukcje warunkowe

	// stary sposób
	if (typ == 'foo' || typ == 'bar') { … }
	 
	// lepiej
	if (/^(foo|bar)$/.test(typ)) { … }
	 
	// przeszukiwanie literału obiektowego
	if (({ foo : 1, bar : 1 })[typ]) { ... }

Nie traktuj jQuery jak czarnej skrzynki

Korzystaj z dokumentacji w postaci kodu źródłowego — dodaj stronę http://bit.ly/jqsource do zakładek i często ją odwiedzaj.

Autor: Rebecca Murphey

Źródło: http://github.com/rmurphey/jqfundamentals

Tłumaczenie: Joanna Liana

Treść tej strony dostępna jest na zasadach licencji CC BY-SA 3.0 US

1 komentarz

  1. Bardzo dobry artykuł ! Od dziś będę zwracał dużo więcej uwagi na wydajność moich skryptów.

    Odpowiedz

Dyskusja

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *