Wprowadzenie
HTML5 zapewnia nam świetne narzędzia do ulepszania wyglądu aplikacji internetowych. Dotyczy to zwłaszcza animacji. Jednak wraz z tą nową mocą pojawiają się też nowe wyzwania. W zasadzie te problemy nie są aż tak nowe i czasem warto zapytać życzliwego sąsiada z biurka, programistę Flasha, jak poradził sobie z podobnymi problemami w przeszłości.
Gdy pracujesz nad animacją, bardzo ważne jest, aby użytkownicy postrzegali ją jako płynną. Należy sobie uświadomić, że płynności animacji nie można uzyskać, po prostu zwiększając liczbę klatek na sekundę ponad próg percepcji. Nasz mózg jest jednak, niestety, sprytniejszy. Dowiesz się, że 30 klatek animacji na sekundę jest znacznie lepsze niż 60 kl./s z kilkoma klatkami, które zostały utracone w połowie. Ludzie nie lubią nierówności.
Z tego artykułu dowiesz się, jakie narzędzia i techniki możesz wykorzystać do ulepszenia swojej aplikacji.
Strategia
Nie chcemy w żaden sposób zniechęcać Cię do tworzenia wspaniałych, atrakcyjnych wizualnie aplikacji w HTML5.
Gdy zauważysz, że wydajność mogłaby być nieco lepsza, wróć tutaj i dowiedz się, jak ulepszyć elementy aplikacji. Oczywiście może to pomóc w tym, aby od razu robić pewne rzeczy dobrze, ale nie pozwól, aby przeszkadzało Ci to w byciu produktywnym.
Lepsza jakość obrazu w przypadku reklam HTML5
Akceleracja sprzętowa
Sprzętowa akceleracja to ważny krok w kierunku ogólnej wydajności renderowania w przeglądarce. Ogólny schemat polega na przekierowaniu zadań, które byłyby obliczane przez główny procesor, do procesora graficznego (GPU) w procesorze graficznym komputera. Może to przynieść ogromne korzyści w zakresie wydajności, a także ograniczyć zużycie zasobów na urządzeniach mobilnych.
Te aspekty dokumentu mogą być przyspieszane przez procesor graficzny.
- Kompilacja ogólnego układu
- Przejścia CSS3
- Przekształcenia 3D w CSS3
- Rysowanie na kanwie
- Rysowanie 3D w WebGL
Przyspieszenie działania canvasa i WebGL to funkcje specjalne, które mogą nie mieć zastosowania w Twojej aplikacji, ale 3 pierwsze aspekty mogą przyspieszyć działanie niemal każdej aplikacji.
Co można przyspieszyć?
Akceleracja GPU polega na przekierowywaniu dobrze zdefiniowanych i konkretnych zadań na sprzęt o specjalnym przeznaczeniu. Ogólny schemat polega na tym, że dokument jest dzielony na wiele „warstw”, które są niezmienne w przypadku aspektów strony, które są przyspieszone. Te warstwy są renderowane za pomocą tradycyjnego potoku renderowania. Następnie GPU służy do łączenia warstw na jednej stronie z zastosowaniem „efektów”, które można przyspieszyć w locie. Możliwym rozwiązaniem jest, że obiekt animowany na ekranie nie wymaga ani jednego „przeformatowania” strony podczas animacji.
Musisz więc ułatwić mechanizmowi renderowania rozpoznanie, kiedy może zastosować magię akceleracji GPU. Na przykład:
Ta metoda działa, ale przeglądarka nie wie, że wykonujesz czynności, które mają być odbierane przez człowieka jako płynna animacja. Zobacz, co się dzieje, gdy osiągniesz ten sam wygląd za pomocą przejść CSS3:
Sposób implementacji tej animacji przez przeglądarkę jest całkowicie ukryty przed deweloperem. Oznacza to, że przeglądarka może stosować sztuczki, takie jak akceleracja GPU, aby osiągnąć określony cel.
Do debugowania przyspieszania GPU w Chrome można użyć 2 przydatnych flag w wierszu poleceń:
--show-composited-layer-borders
wyświetla czerwone obramowanie wokół elementów, które są modyfikowane na poziomie GPU. Dobre rozwiązanie, jeśli chcesz, aby manipulacje odbywały się na poziomie GPU.--show-paint-rects
wszystkie zmiany poza GPU są malowane, co powoduje wyświetlanie jasnej obwódki wokół wszystkich obszarów, które są ponownie malowane. Możesz zobaczyć, jak przeglądarka optymalizuje obszary malowania.
Safari ma podobne flagi czasu wykonywania, które zostały opisane tutaj.
Przejścia CSS3
Przejścia CSS ułatwiają animację stylów, ale są też inteligentną funkcją zwiększającą wydajność. Ponieważ przejście CSS jest zarządzane przez przeglądarkę, można znacznie poprawić jakość animacji i w wielu przypadkach przyspieszyć ją za pomocą sprzętu. Obecnie WebKit (Chrome, Safari, iOS) korzysta z przekształceń CSS przyspieszonych sprzętowo, ale ta funkcja szybko trafia też do innych przeglądarek i platform.
Aby stworzyć potężne kombinacje, możesz użyć zdarzeń transitionEnd
, ale obecnie rejestrowanie wszystkich obsługiwanych zdarzeń zakończenia przejścia oznacza obserwowanie zdarzenia webkitTransitionEnd transitionend oTransitionEnd
.
Wiele bibliotek wprowadziło interfejsy API animacji, które wykorzystują przejścia, jeśli są dostępne, a w przeciwnym razie używają standardowej animacji w stylu DOM. scripty2, YUI transition, jQuery animate enhanced.
CSS3 Translate
Z pewnością zdarzyło Ci się już animować pozycję X/Y elementu na stronie. Prawdopodobnie zmieniono właściwości „Lewo” i „Góra” stylu wbudowanego. W przypadku transformacji 2D możemy użyć funkcji translate()
, aby odtworzyć to zachowanie.
Możemy połączyć to z animacją DOM, aby uzyskać jak najlepszy efekt.
<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>
Używamy Modernizr do testowania funkcji CSS 2D Transforms i CSS Transitions. Jeśli tak, użyjemy translate do zmiany pozycji. Jeśli animacja jest tworzona za pomocą przejścia, przeglądarka może ją przyspieszyć za pomocą akceleracji sprzętowej. Aby jeszcze bardziej ukierunkować przeglądarkę, użyjemy „magicznego kodu CSS” z powyższego przykładu.
Jeśli nasza przeglądarka ma mniejsze możliwości, do przeniesienia elementu użyjemy jQuery. Możesz użyć wtyczki jQuery Transform polyfill autorstwa Louis-Remi Babe, aby zautomatyzować ten proces.
window.requestAnimationFrame
requestAnimationFrame
został wprowadzony przez Mozillę i udoskonalany przez WebKit w celu zapewnienia natywnego interfejsu API do uruchamiania animacji, niezależnie od tego, czy są one oparte na DOM/CSS, <canvas>
czy WebGL. Przeglądarka może optymalizować równoległe animacje w jednym cyklu ponownego przepływu i odświeżania, co zwiększa wierność animacji. Przykładem mogą być animacje oparte na JS zsynchronizowane z przejściami CSS lub SMIL SVG. Jeśli animacja jest odtwarzana w karcie, która nie jest widoczna, przeglądarka nie będzie jej odtwarzać, co oznacza mniejsze wykorzystanie procesora, karty graficznej i pamięci, a w konsekwencji dłuższy czas pracy na baterii.
Więcej informacji o tym, jak i dlaczego używać metody requestAnimationFrame
, znajdziesz w artykule Paula Irisha Metoda requestAnimationFrame do inteligentnej animacji.
Profilowanie
Gdy odkryjesz, że szybkość działania aplikacji można poprawić, czas zająć się profilowaniem, aby dowiedzieć się, gdzie optymalizacja przyniesie największe korzyści. Optymalizacja często ma negatywny wpływ na możliwość utrzymania kodu źródłowego, dlatego należy stosować ją tylko w razie potrzeby. Profilowanie pozwala określić, które części kodu przyniosą największe korzyści, gdy ich wydajność zostanie poprawiona.
Profilowanie kodu JavaScript
Profilatory JavaScriptu podają przegląd wydajności aplikacji na poziomie funkcji JavaScriptu, mierząc czas wykonywania każdej funkcji od początku do końca.
Całkowity czas wykonania funkcji to łączny czas jej wykonywania od początku do końca. Czas netto wykonania to czas brutto wykonania pomniejszony o czas wykonania funkcji wywołanych z tej funkcji.
Niektóre funkcje są wywoływane częściej niż inne. Narzędzie do profilowania zwykle podaje czas wykonania wszystkich wywołań, a także średni, minimalny i maksymalny czas wykonania.
Więcej informacji znajdziesz w dokumentacji dotyczącej profilowania w Narzędziach deweloperskich Chrome.
DOM
Wydajność kodu JavaScript ma duży wpływ na płynność i szybkość działania aplikacji. Pamiętaj, że profiler JavaScript mierzy czas wykonywania kodu JavaScript, ale pośrednio również czas poświęcony na operacje DOM. Te operacje DOM są często główną przyczyną problemów ze skutecznością.
function drawArray(array) {
for(var i = 0; i < array.length; i++) {
document.getElementById('test').innerHTML += array[i]; // No good :(
}
}
Przykładowo w powyższym kodzie prawie wcale nie jest tracony czas na wykonywanie kodu JavaScript. Nadal jest bardzo prawdopodobne, że funkcja drawArray pojawi się w Twoich profilach, ponieważ współpracuje ona z DOM w bardzo nieefektywny sposób.
Porady i wskazówki
Funkcje anonimowe
Anonimowe funkcje są trudne do przeanalizowania, ponieważ nie mają nazwy, pod którą mogłyby się wyświetlać w profilu. Możesz to zrobić na 2 sposoby:
$('.stuff').each(function() { ... });
przepisać na:
$('.stuff').each(function workOnStuff() { ... });
Niewiele osób wie, że JavaScript obsługuje nazywanie wyrażeń funkcji. Dzięki temu będą one wyświetlane prawidłowo w profilu. Jest w nim jednak pewien problem: wyrażenie nazwane umieszcza nazwę funkcji w bieżącym zakresie leksykalnym. Może to powodować zastępowanie innych symboli, więc należy zachować ostrożność.
Profilowanie długich funkcji
Wyobraź sobie, że masz długą funkcję i podejrzewasz, że jej niewielka część może być przyczyną problemów z wydajnością. Istnieją 2 sposoby sprawdzenia, która część jest problemowa:
- Prawidłowa metoda: przerzuć kod, aby nie zawierał długich funkcji.
- Zła metoda wykonywania zadań: dodaj do kodu instrukcje w formie wywołujących się samych siebie funkcji nazwanych. Jeśli zachowasz ostrożność, nie spowoduje to zmiany semantyki, a części funkcji będą widoczne w profilerze jako osobne funkcje:
js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... }
Nie zapomnij usunąć tych dodatkowych funkcji po zakończeniu profilowania. Możesz też użyć ich jako punktu wyjścia do refaktoryzacji kodu.
Profilowanie DOM
Najnowsze narzędzia deweloperskie w przeglądarce Chrome Web Inspector zawierają nowy widok osi czasu, który pokazuje działania na niskim poziomie wykonywane przez przeglądarkę. Możesz używać tych informacji do optymalizacji operacji DOM. Staraj się zmniejszyć liczbę „działań”, które przeglądarka musi wykonać podczas wykonywania kodu.
Widok osi czasu może zawierać ogromną ilość informacji. Dlatego warto tworzyć minimalne przypadki testowe, które można wykonać niezależnie.
Obraz powyżej pokazuje wynik widoku osi czasu dla bardzo prostego skryptu. Panel po lewej stronie zawiera operacje wykonywane przez przeglądarkę w kolejności chronologicznej, a oś czasu w panelu po prawej stronie pokazuje rzeczywisty czas trwania poszczególnych operacji.
Więcej informacji o widoku osi czasu Alternatywnym narzędziem do profilowania w Internet Explorerze jest DynaTrace Ajax Edition.
Strategie profilowania
Wyróżnianie aspektów
Jeśli chcesz przeprowadzić profilowanie aplikacji, wybierz aspekty jej działania, które mogą powodować spowolnienie, i spróbuj je zminimalizować. Następnie spróbuj przeprowadzić profilowanie, które wykona tylko te części kodu, które są istotne dla tych aspektów aplikacji. Dzięki temu łatwiej będzie Ci interpretować dane profilowania, ponieważ nie będą one mieszane z ścieżkami kodu, które nie mają związku z rzeczywistym problemem. Dobre przykłady poszczególnych aspektów aplikacji to:
- Czas uruchamiania (aktywuj profilujący, ponownie załaduj aplikację, zaczekaj na zakończenie inicjalizacji, zatrzymaj profilujący).
- Kliknij przycisk i subsequentną animację (uruchom profilowanie, kliknij przycisk, poczekaj na zakończenie animacji, zatrzymaj profilowanie).
Profilowanie GUI
Wykonywanie tylko odpowiedniej części aplikacji może być trudniejsze w programie z interfejsem graficznym niż w przypadku optymalizacji np. śledzenia promieni w silniku 3D. Jeśli np. chcesz przeanalizować, co dzieje się po kliknięciu przycisku, możesz przypadkowo wywołać niezwiązane zdarzenia mouseover, które spowodują, że wyniki będą mniej jednoznaczne. Staraj się tego unikać. :)
Interfejs programowy
Dostępne jest też programowe API do aktywowania debugera. Dzięki temu możesz dokładnie kontrolować, kiedy rozpoczyna się i kończy profilowanie.
Aby rozpocząć profilowanie:
console.profile()
Aby zatrzymać profilowanie:
console.profileEnd()
Powtarzalność
Podczas profilowania upewnij się, że możesz odtworzyć wyniki. Dopiero wtedy będziesz mieć pewność, że optymalizacja przyniosła oczekiwane efekty. Profilowanie na poziomie funkcji jest też wykonywane w kontekście całego komputera. Nie jest to nauka ścisła. Na poszczególne uruchomienia profilu może mieć wpływ wiele innych czynności wykonywanych na komputerze:
- niezwiązany z tym licznik w Twojej aplikacji, który działa podczas pomiaru czegoś innego.
- Zbieracz wykonuje swoją pracę.
- Inna karta w przeglądarce, która ciężko pracuje w tym samym wątku operacyjnym.
- Inny program na komputerze obciąża procesor, przez co aplikacja działa wolniej.
- Nagłe zmiany pola grawitacyjnego Ziemi.
W ramach jednej sesji profilowania warto też wykonać ten sam ścieżkę kodu kilka razy. W ten sposób zmniejszysz wpływ powyższych czynników, a wolne fragmenty mogą jeszcze wyraźniej się wyróżniać.
Pomiar, poprawa, pomiar
Gdy zauważysz w programie wolno działający fragment, zastanów się, jak poprawić jego działanie. Po zmianie kodu ponownie utwórz profil. Jeśli jesteś zadowolony z rezultatu, przejdź dalej. Jeśli nie widzisz poprawy, prawdopodobnie powinieneś cofnąć zmianę, a nie pozostawić jej „bo to nic nie szkodzi”.
Strategie optymalizacji
Minimalizowanie interakcji z DOM
Jednym z częstych sposobów zwiększania szybkości aplikacji klienckich w internecie jest minimalizowanie interakcji z DOM. Chociaż szybkość silników JavaScriptu wzrosła o wiele rzędów wielkości, dostęp do DOM nie przyspieszył w takim samym stopniu. Z bardzo praktycznych powodów nigdy nie dojdzie do tego, by to się stało (takie rzeczy jak układanie i rysowanie na ekranie zajmują czas).
Pamięć podręczna węzłów DOM
Za każdym razem, gdy pobierasz węzeł lub listę węzłów z DOM, zastanów się, czy możesz ich użyć ponownie w późniejszym obliczeniu (lub nawet w kolejej iteracji pętli). Zwykle tak się dzieje, o ile nie dodajesz ani nie usuwasz węzłów w odpowiednim obszarze.
Przed:
function getElements() {
return $('.my-class');
}
Po:
var cachedElements;
function getElements() {
if (cachedElements) {
return cachedElements;
}
cachedElements = $('.my-class');
return cachedElements;
}
Wartości atrybutów w pamięci podręcznej
Wartości atrybutów możesz przechowywać w pamięci podręcznej w taki sam sposób jak węzły DOM. Wyobraź sobie, że animujesz atrybut stylu węzła. Jeśli wiesz, że tylko Ty (w tej części kodu) będziesz modyfikować ten atrybut, możesz przechowywać w pamięci podręcznej jego ostatnią wartość w każdej iteracji, aby nie trzeba było jej wielokrotnie odczytywać.
Przed:
setInterval(function() {
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
ele.css('left', (left + 5) + 'px');
}, 1000 / 30);
Po:
js
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
setInterval(function() {
left += 5;
ele.css('left', left + 'px');
}, 1000 / 30);
Przenoszenie manipulacji DOM poza pętle
Pętle są często punktami krytycznymi pod kątem optymalizacji. Spróbuj znaleźć sposoby na oddzielenie faktycznego przetwarzania liczb od pracy z DOM. Często można wykonać obliczenia, a potem zastosować wszystkie wyniki za jednym razem.
Przed:
document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
document.getElementById('target').innerHTML += val;
}
Po:
var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');
Narysuj ponownie i Przepływ danych
Jak już wspomnieliśmy, dostęp do DOM jest stosunkowo wolny. Kod staje się bardzo powolny, gdy odczytuje wartość, która musi zostać ponownie obliczona, ponieważ kod niedawno zmodyfikował coś powiązanego w DOM. Dlatego należy unikać mieszania dostępu do odczytu i zapisu do DOM. Najlepiej, gdyby kod był zawsze grupowany w 2 etapy:
- Etap 1. Odczytaj wartości DOM potrzebne do działania kodu
- Etap 2. Zmień DOM
Unikaj programowania wzorców takich jak:
- Etap 1. Odczytaj wartości DOM
- Etap 2. Zmień DOM
- Etap 3. Przeczytaj więcej
- Etap 4. Zmień DOM gdzie indziej.
Przed:
function paintSlow() {
var left1 = $('#thing1').css('left');
$('#otherThing1').css('left', left);
var left2 = $('#thing2').css('left');
$('#otherThing2').css('left', left);
}
Po:
function paintFast() {
var left1 = $('#thing1').css('left');
var left2 = $('#thing2').css('left');
$('#otherThing1').css('left', left);
$('#otherThing2').css('left', left);
}
Te wskazówki należy wziąć pod uwagę w przypadku działań wykonywanych w ramach jednego kontekstu wykonywania kodu JavaScript. (np. w modułach obsługi zdarzeń lub interwałów albo podczas obsługi odpowiedzi ajax).
Wykonywanie funkcji paintSlow()
z powyższego przykładu powoduje wyświetlenie tego obrazu:
Przejście na szybszą implementację powoduje wyświetlenie tego obrazu:
Te obrazy pokazują, że zmiana kolejności dostępu kodu do DOM może znacznie poprawić wydajność renderowania. W takim przypadku oryginalny kod musi ponownie obliczyć style i układ strony, aby uzyskać ten sam wynik. Podobną optymalizację można zastosować do praktycznie wszystkich kodów „w rzeczywistych warunkach” i osiągnąć naprawdę spektakularne wyniki.
Więcej informacji: Renderowanie: ponowne malowanie, ponowne przepływanie/ponowne układanie, zmiana stylu (ang.) (autor: Stoyan Stefanov)
Odświeżanie i pętla zdarzeń
Wykonanie kodu JavaScript w przeglądarce odbywa się zgodnie z modelem „pętli zdarzeń”. Domyślnie przeglądarka jest w stanie „nieczynności”. Ten stan może zostać przerwany przez zdarzenia wywołane przez interakcje użytkownika lub takie elementy jak liczniki czasu JavaScript czy wywołania zwrotne Ajax. Gdy w takim miejscu nastąpi przerwanie, przeglądarka zazwyczaj czeka, aż kod JavaScriptu zostanie ukończony i odświeży ekran (mogą występować wyjątki w przypadku bardzo długiego kodu JavaScriptu lub w przypadkach takich jak alerty, które skutecznie przerywają jego wykonywanie).
Konsekwencje
- Jeśli cykle animacji JavaScriptu zajmują więcej niż 1/30 sekundy, nie będzie można tworzyć płynnych animacji, ponieważ przeglądarka nie będzie ponownie rysować podczas wykonywania kodu JavaScript. Jeśli chcesz też obsługiwać zdarzenia użytkownika, musisz działać znacznie szybciej.
- Czasami opóźnienie niektórych działań JavaScript może być przydatne.
np.
setTimeout(function() { ... }, 0)
To oznacza, że przeglądarka ma wykonać funkcję wywołania zwrotnego, gdy tylko pętla zdarzeń znów będzie nieaktywna (w praktyce niektóre przeglądarki będą czekać co najmniej 10 ms). Pamiętaj, że spowoduje to utworzenie 2 cykli wykonywania kodu JavaScript, które będą bardzo blisko siebie pod względem czasowym. Oba te czynniki mogą powodować ponowne malowanie ekranu, co może podwoić łączny czas malowania. To, czy faktycznie spowoduje to 2 odświeżenia, zależy od heurystyki w przeglądarce.
Wersja standardowa:
function paintFast() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
$('#otherThing2').css('height', '20px');
}
Dodajmy opóźnienie:
function paintALittleLater() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
setTimeout(function() {
$('#otherThing2').css('height', '20px');
}, 10)
}
Wersja z opóźnieniem pokazuje, że przeglądarka wyświetla obraz dwukrotnie, mimo że 2 zmiany na stronie trwają tylko 1/100 s.
Leniwa inicjalizacja
Użytkownicy chcą, aby aplikacje internetowe wczytywały się szybko i sprawnie działały. Jednak użytkownicy mają różne progi tego, co uważają za wolne, w zależności od wykonywanych czynności. Przykładowo aplikacja nigdy nie powinna wykonywać dużej ilości obliczeń w przypadku zdarzenia mouseover, ponieważ może to pogorszyć komfort użytkownika, gdy ten będzie nadal poruszać kursorem. Użytkownicy są jednak przyzwyczajeni do krótkiego opóźnienia po kliknięciu przycisku.
Dlatego warto przenieść kod inicjalizacji do wykonania jak najpóźniej (np. gdy użytkownik kliknie przycisk aktywujący określony komponent aplikacji).
Przed:
js
var things = $('.ele > .other * div.className');
$('#button').click(function() { things.show() });
Po:
js
$('#button').click(function() { $('.ele > .other * div.className').show() });
Delegowanie zdarzeń
Rozmieszczanie na stronie elementów sterujących zdarzeniem może zająć stosunkowo dużo czasu i być żmudne, gdy elementy są dynamicznie zastępowane, co wymaga ponownego dołączania elementów sterujących do nowych elementów.
W tym przypadku rozwiązaniem jest użycie techniki zwanej delegowaniem zdarzeń. Zamiast dołączać do elementów poszczególne metody obsługi zdarzeń, można wykorzystać naturę przenoszenia zdarzeń w przeglądarce, dołączając metodę obsługi zdarzeń do węzła nadrzędnego i sprawdzając węzeł docelowy zdarzenia, aby sprawdzić, czy zdarzenie jest interesujące.
W jQuery można to łatwo wyrazić w ten sposób:
$('#parentNode').delegate('.button', 'click', function() { ... });
Kiedy nie używać przekazywania zdarzeń
Czasami może być odwrotnie: używasz delegowania zdarzeń i masz problem z wydajnością. Delegowanie zdarzeń pozwala na stały czas inicjowania o zmiennej złożoności. Jednak koszt sprawdzenia, czy zdarzenie jest interesujące, musi być płacony za każde wywołanie tego zdarzenia. Może to być kosztowne, zwłaszcza w przypadku zdarzeń, które występują często, np. „przejście kursorem” czy nawet „poruszenie myszką”.
Typowe problemy i ich rozwiązania
Zadania, które wykonuję w $(document).ready
, zajmują dużo czasu
Osobista rada Malte: nigdy nie rób niczego w $(document).ready
. Spróbuj dostarczyć dokument w ostatecznej formie. Możesz rejestrować detektory zdarzeń, ale tylko za pomocą selektora identyfikatora lub delegowania zdarzeń. W przypadku drogich zdarzeń, takich jak „mousemove”, opóźnij rejestrację do momentu, gdy będą potrzebne (zdarzenie mouseover na odpowiednim elemencie).
Jeśli naprawdę musisz coś zrobić, np. wysłać żądanie Ajax, aby uzyskać rzeczywiste dane, a potem wyświetlić ładną animację, możesz umieścić animację jako identyfikator URI danych, jeśli jest to animowany GIF lub coś podobnego.
Odkąd dodałem na stronie film Flash, wszystko jest bardzo wolne
Dodanie do strony treści Flash zawsze spowalnia nieco renderowanie, ponieważ przeglądarka i wtyczka Flash muszą „uzgodnić” ostateczny układ okna. Jeśli nie możesz całkowicie uniknąć umieszczania Flasha na swoich stronach, ustaw parametr „wmode” Flasha na wartość „window” (co jest domyślną wartością). Spowoduje to wyłączenie możliwości łączenia elementów HTML i Flash (nie będzie można zobaczyć elementu HTML, który znajduje się nad filmem Flash, a film Flash nie może być przezroczysty). Może to być uciążliwe, ale znacznie poprawi skuteczność. Zobacz na przykład, jak strona youtube.com starannie unika umieszczania warstw nad głównym odtwarzaczem filmu.
Zapisuje dane w localStorage, a teraz aplikacja się zacina
Zapisywanie do localStorage to operacja synchroniczna, która wymaga uruchomienia dysku twardego. Podczas tworzenia animacji nigdy nie należy wykonywać operacji synchronicznych „długotrwałych”. Przenieś dostęp do localStorage do miejsca w kodzie, w którym masz pewność, że użytkownik jest nieaktywny i nie są wyświetlane żadne animacje.
Profilowanie wskazuje, że selektor jQuery jest bardzo powolny
Najpierw musisz się upewnić, że selektor można uruchomić za pomocą funkcji document.querySelectorAll. Możesz to sprawdzić w konsoli JavaScript. Jeśli jest to wyjątek, zmień selektor, aby nie używać żadnego specjalnego rozszerzenia Twojego frameworka JavaScript. Przyspieszy to działanie selektora w nowoczesnych przeglądarkach.
Jeśli to nie pomoże lub jeśli chcesz też mieć szybki dostęp w nowoczesnych przeglądarkach, postępuj zgodnie z tymi wskazówkami:
- Podaj jak najwięcej szczegółów po prawej stronie selektora.
- Jako najdalej wysunięty element selektora użyj nazwy tagu, którego nie używasz często.
- Jeśli nic nie pomaga, rozważ przeredagowanie treści, aby można było użyć selektora identyfikatora.
Wszystkie te manipulacje DOM zajmują dużo czasu
Wstawianie, usuwanie i aktualizowanie wielu węzłów DOM może być bardzo powolne. Można to zoptymalizować, generując długi ciąg znaków HTML i używanie domNode.innerHTML = newHTML
do zastępowania starych treści. Pamiętaj, że może to negatywnie wpłynąć na możliwość utrzymania aplikacji i stworzyć linki pamięci w IE, więc bądź ostrożny.
Innym częstym problemem jest to, że kod inicjalizacji może generować dużo kodu HTML. Przykład: wtyczka jQuery, która zamienia pole wyboru na kilka elementów div, ponieważ projektanci nie znają najlepszych praktyk dotyczących UX. Jeśli naprawdę zależy Ci na szybkim wczytywaniu się strony, nigdy tego nie rób. Zamiast tego prześlij całą znacznikowaną treść po stronie serwera w jej ostatecznej formie. To rozwiązanie również ma wiele wad, więc zastanów się, czy szybkość jest warta poświęcenia.
Narzędzia
- JSPerf – testowanie porównawcze małych fragmentów kodu JavaScript
- Firebug – do profilowania w Firefoksie
- Narzędzia deweloperskie Google Chrome (dostępne jako WebInspector w Safari)
- DOM Monster – optymalizacja wydajności DOM
- DynaTrace Ajax Edition – do profilowania i optymalizacji odświeżania w Internet Explorerze