Rozdział 9. Wydajność — najlepsze rozwiązania
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 jest dostępna na zasadach licencji CC BY-SA 3.0 US
26 października 2012
Bardzo dobry artykuł ! Od dziś będę zwracał dużo więcej uwagi na wydajność moich skryptów.