Optymalizuj wykonywanie JavaScriptu

JavaScript często wyzwala zmiany wizualne. Czasami ma to związek bezpośrednio z manipulacją stylem, a czasem z obliczeniami skutkującymi zmianami wizualnymi, takimi jak wyszukiwanie lub sortowanie danych. Częstą przyczyną problemów z wydajnością jest zbyt długi lub długo działający kod JavaScript. Postaraj się zminimalizować to zjawisko.

Paula Lewisa
Paul Lewis

JavaScript często wyzwala zmiany wizualne. Czasami dzieje się tak bezpośrednio w wyniku manipulacji stylem, a czasem ze zmian wizualnych, takich jak wyszukiwanie lub sortowanie danych. Częstą przyczyną problemów z wydajnością jest zbyt długi lub długo działający kod JavaScript. Postaraj się zminimalizować to zjawisko.

Profilowanie wydajności JavaScriptu może być czymś w rodzaju sztuki, ponieważ tworzony przez Ciebie JavaScript nie różni się od tego, który jest rzeczywiście wykonywany. Nowoczesne przeglądarki korzystają z kompilatorów JIT oraz wszelkich optymalizacji i sztuczek, które pozwalają przyspieszyć wykonywanie kodu, co w znacznym stopniu zmienia dynamikę kodu.

Jednak jest kilka rzeczy, które możesz zrobić, aby zadbać o prawidłowe wykonywanie kodu JavaScript w swoich aplikacjach.

Podsumowanie

  • Unikaj ustawień setTimeout i setInterval w przypadku aktualizacji wizualnych. Zamiast tego zawsze używaj metody requestAnimationFrame.
  • Przenieś działający od dawna JavaScript z wątku głównego do instancji roboczych.
  • Używaj mikrozadań, aby wprowadzać zmiany DOM w kilku klatkach.
  • Oceń wpływ JavaScriptu za pomocą osi czasu i programu profilującego JavaScript w Narzędziach deweloperskich w Chrome.

Użyj requestAnimationFrame do zmian wizualnych

Gdy na ekranie widać zmiany wizualne, chcesz wykonać pracę w momencie odpowiednim dla przeglądarki, czyli dokładnie na początku ramki. Jedynym sposobem zagwarantowania, że JavaScript będzie uruchamiał się na początku klatki, jest użycie requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

Platformy lub przykłady mogą wykorzystywać setTimeout lub setInterval do wprowadzania zmian wizualnych, np. animacji, ale problem polega na tym, że wywołanie zwrotne będzie działać w pewnym punkcie w ramce, być może na końcu, i często może to prowadzić do pomijania klatki, a w rezultacie do zacinania się.

setTimeout powodują pominięcie ramki w przeglądarce.

W języku angielskim jQuery używała setTimeout na potrzeby zachowania animate. W wersji 3 została ona zmieniona na requestAnimationFrame. Jeśli używasz starszej wersji biblioteki jQuery, możesz zainstalować poprawkę, aby używać w niej requestAnimationFrame. Zdecydowanie zalecamy to rozwiązanie.

Zmniejsz złożoność lub użyj instancji roboczych

JavaScript działa w głównym wątku przeglądarki, równolegle z obliczeniami stylu, układem i w wielu przypadkach renderowaniem. Jeśli JavaScript działa przez dłuższy czas, zablokuje inne zadania, co może spowodować pomijanie klatek.

Musisz taktycznie kierować się działaniem JavaScriptu i czasem jego trwania. Na przykład w animacji takiej jak przewijanie, najlepiej zadbać o to, aby JavaScript znajdował się w obszarze 3–4 ms. Jeśli przekroczysz ten limit, ryzykujesz, że zajmie to zbyt dużo czasu. Podczas bezczynności możesz się zrelaksować.

W wielu przypadkach możesz przenieść czystą pracę obliczeniową do instancji roboczych, jeśli na przykład nie wymagają one dostępu DOM. Manipulowanie danymi lub przemierzanie ich, takie jak sortowanie lub wyszukiwanie, dobrze sprawdza się w tym modelu, podobnie jak w przypadku wczytywania i generowania modelu.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

Nie wszystkie prace pasują do tego modelu: instancje robocze nie mają dostępu do modelu DOM. W przypadku głównego wątku musisz zastosować metodę grupowania polegającą na podziale większych zadań na mikrozadania, które nie trwają dłużej niż kilka milisekund, i wykonywania w każdej klatce wewnątrz modułów obsługi requestAnimationFrame.

Takie podejście ma wpływ na wygodę użytkowników i interfejs. Musisz dopilnować, aby użytkownik wiedział o tym, że dane zadanie jest przetwarzane – użyj wskaźnika postępu lub aktywności. W każdym przypadku takie podejście sprawi, że główny wątek aplikacji pozostanie bezpłatny, a aplikacja będzie reagowała na interakcje użytkowników.

Zapoznaj się z „podatkiem od ramek” w przypadku JavaScriptu

Oceniając platformę, bibliotekę lub własny kod, musisz także oceniać, ile kosztuje uruchomienie kodu JavaScript w każdej klatce. Jest to szczególnie ważne, jeśli wykonujesz animacje, które mają kluczowe znaczenie dla wydajności, takie jak przejścia lub przewijanie.

Panel wydajności w Narzędziach deweloperskich w Chrome to najlepszy sposób na zmierzenie kosztów JavaScriptu. Zwykle są to rekordy niskiego poziomu, na przykład:

Nagranie z wydajności w Narzędziach deweloperskich w Chrome

Sekcja Główne zawiera wykres płomieniowy wywołań JavaScriptu, dzięki czemu możesz dokładnie przeanalizować, które funkcje zostały wywołane i ile czasu zajęło każdej z nich.

Dzięki tym informacjom możesz ocenić wpływ JavaScriptu na Twoją aplikację oraz znaleźć i naprawić elementy, w których wykonywanie funkcji trwa zbyt długo. Jak już wspomnieliśmy, należy usunąć długo działający kod JavaScript lub, jeśli nie jest to możliwe, przenieść go do Web Worker, co spowoduje zwolnienie głównego wątku na potrzeby kontynuowania innych zadań.

Aby dowiedzieć się, jak korzystać z panelu wydajności, przeczytaj artykuł Pierwsze kroki z analizą wydajności środowiska wykonawczego.

Unikaj mikrooptymalizacji kodu JavaScript

Fajnie jest wiedzieć, że przeglądarka jest w stanie wykonać jedną wersję 100 razy szybciej niż inne. Na przykład żądanie offsetTop elementu jest szybsze niż przetwarzanie getBoundingClientRect(). Jest jednak prawie zawsze prawdziwe, że takie funkcje będą wywoływać niewielką liczbę razy na klatkę, dlatego zwykle nie marnuje czasu na skupianie się na tym aspekcie wydajności JavaScriptu. Oszczędzasz zwykle tylko ułamki milisekund.

Jeśli tworzysz grę lub wymagasz drogiej aplikacji, prawdopodobnie jesteś wyjątkiem od tej zasady, ponieważ zazwyczaj w jednej klatce trzeba wykonać wiele obliczeń, więc wszystko jest w porządku.

Krótko mówiąc, należy bardzo uważać na mikrooptymalizacje, ponieważ zazwyczaj nie pasują one do tworzonej aplikacji.