Skrócenie czasu ładowania stron WWW — optymalizacja skryptów JS
Ostatnio Webstop.pl rozpoczął serię artykułów związanych z internetowym know-how. Pierwszy z nich omawia różne sposoby na skrócenie czasu ładowania stron. A gra jest warta świeczki, bo dzisiejszy internauta jest bardzo niecierpliwy i szybko się zniechęca. Jeśli nie wiesz CO należy zrobić, aby strony wczytywały się szybciej to odsyłam do artykułu. Natomiast jeśli chcesz się dowiedzieć JAK to zrobić, to dobrze trafiłeś!
JavaScript jest wszędzie
Zacznijmy od tego, że optymalizację czasu wyświetlania się strony powinniśmy zacząć po stronie użytkownika. Jeśli policzymy czas od momentu wpisania adresu i wciśnięcia ENTER aż do wyświetlenia całej strony to wyjdzie nam, że około 80-90% czasu zajmą wszystkie czynności związane z wyświetleniem frontendu, tzn. pobranie dodatkowych obrazków, uruchomienie skryptów, załadowanie dodatkowych ramek itd. Tylko 10-20% to czas w jakim serwer tworzy samą zawartość strony (np. wykonując skrypty PHP). No dobrze, ale co najbardziej wydłuża czas wczytywania się stron WWW? Zdecydowanie JavaScript. W dalszej części spróbujemy jakoś temu zaradzić.
W tej chwili niemal 100% stron WWW używa różnych skryptów: od javascriptowych wodotrysków w interfejsie, po trackery i zewnętrzne reklamy. Najczęściej skrypty JS używane są jako różne wklejki na stronie. Wszystko jest proste: kopiuj-wklej w kod HTML strony. Niestety z punktu widzenia wydajności jest to najczęściej spory błąd.
Dla przeciętnego użytkownika gotowa i załadowana strona to taka, którą już można w całości zobaczyć wraz ze wszystkimi jej składnikami (obrazki, flashe itd). Przeglądarka świetnie sobie radzi żeby zrobić to jak najszybciej: ściąga kilka elementów równolegle i wyświetla je jak tylko do niej dotrą, nawet jeszcze w trakcie ściągania. Inaczej jest jednak ze skryptami. Skrypty ściągane są w całości i zaraz po ściągnięciu uruchamiane. Co więcej, w trakcie działania skryptu przeglądarka nie wykonuje nic innego. Czyli np. nie ściąga pozostałych składników strony tylko cierpliwie czeka aż skrypt zrobi swoje.
Co to oznacza? Oto kilka case studies:
- Strona korzysta z zewnętrznego skryptu gemiusa umieszczonego w sekcji “head” strony. Gemius ma akurat krótką awarię i skrypt jest niedostępny. Przeglądarka zaczyna wczytywac stronę ale czeka na skrypt. Po kilku sekundach następuje timeout, ale przez ten czas wszystko stoi w miejscu a użytkownik widzi pustą stronę.
- Nawigacja na stronie oparta jest o panele z zakładkami (tab-based interface). Przełączanie paneli obsługuje skrypt JS, który został umieszczony w kodzie HTML tuż za elementami
tworzącymi panele. Użytkownik widzi wczytującą się w miarę płynnie stronę, jednak w momencie pokazania się zakładek następuje wyraźne przerwanie wczytywania i przez chwilę nic się nie dzieje.
- Strona wykorzystuje zewnętrzny widget umieszczony w panelu bocznym. Widget został wstawiony wg. instrukcji od dostawcy: wklej kod <script type=”text/javascript”> document.write(”….”) </script> Użytkownik po wejściu na taką stronę widzi wczytujące się elementy aż do chwili pojawienia się panelu bocznego, następuje dłuższa chwila przerwy, po czym dopiero zaczyna się wyświetlać reszta strony.
Takich sytuacji jest mnóstwo, ale wszystkie mają wspólną przyczynę: jest nią uruchamianie czasochłonnego skryptu w trakcie ładowania strony.
Od czego zacząć?
Po pierwsze przyjrzymy się, które elementy najbardziej spowalniają stronę. Polecam dwa narzędzia: firebug (dodatek do firefoxa) lub IBM Page Detailer (działa zarówno z IE jak i FF). Żeby nabrać trochę wprawy zobaczmy co się dzieje w przypadku naszych case studies:
W przykładzie pierwszym wczytywanie elementów strony wygląda tak:
Widzimy, że drugi element (skrypt statystyk umieszczony w sekcji ) wczytuje się 12 sekund. Przez ten czas przeglądarka czeka i nie wczytuje kolejnych fragmentów strony. Element trzeci ładowany jest dopiero po zakończeniu wczytywania i uruchomieniu skryptu. Efekt: przez 12 s użytkownik widzi pustą stronę.
W przykładzie drugim mamy skrypt umieszczony gdzieś w połowie sekcji . I podobnie jak poprzednio, przeglądarka parsując kod HTML natrafia na skrypt i zaczyna go wczytywać, przerywając inne czynności.
W przykładzie trzecim mamy 3 widgety, każdy z nich ładuje się 2 sekundy:
Charakterystyczne są tutaj “schodki” pokazujące kolejność ładowania się skryptów: każdy następny element czeka na załadowanie się poprzedniego. Nie ma tu mowy o równoległym wczytywaniu. Cała strona zacznie się wyświetlać dopiero po 6 s. Polecam zapamiętać sobie te “schodki”, bo będą się później pojawiać wszędzie tam, gdzie skrypty powodują blokowanie ładowania się strony.
No tak, ale co teraz?
Przejdźmy wprost do rozwiązania. Najlepiej by było, gdyby zmusić wszystkie skrypty do równoległego wczytywania i uruchamiania się PO załadowaniu całej treści strony. Niestety czasami mamy kilka skryptów które potrzebne są na etapie ładowania treści. Musimy zatem:
- wydzielić te skrypty, które muszą być uruchomione na etapie wczytywania strony
- wszystkie pozostałe załadować po wyświetleniu strony
- ładować skrypty równolegle w tym samym czasie, bez blokowania
W pierwszym punkcie wszystko zależy od strony, ale im mniej skryptów w trakcie ładowania tym lepiej. No i żadnych skryptów z zewnętrznych serwerów. Żadnych.
W punkcie drugim, jeśli na stronie korzystamy z jQuery to najwygodniej skorzystać z $(document).ready() np w taki sposób:
1
2
3 $(document).ready(function() {
load_all();
});
Jeśli nie chcemy ładować całego jQuery to można skorzystać z gotowych skryptów uruchamianych po załadowaniu struktury DOM. Nie będę wnikał w szczegóły, tylko zainteresowanych odsyłam do bloga Riddle’a, który już to opisał, a w szczególności do tego skryptu.
Pozostało najciekawsze, czyli punkt trzeci. Okazuje się, że jest na to parę sposobów. Tutaj skupimy się na jednym, chyba najciekawszym: dynamicznym ładowaniu skryptów. Spójrzmy na taki kod:
1
2
3
4
5
6 var p=document.getElementsByTagName("HEAD")[0];
var c=document.createElement("script");
c.type="text/javascript";
c.src="http://host/skrypt.js";
p.appendChild(c)
Tworzy on tu dynamicznie element <SCRIPT> i umieszcza go w sekcji <HEAD> strony. Wszystko proste i jasne, ale najciekawsze jest to, że ściągnięcie i uruchomienie skryptu następuje w chwili wykonania linijki p.appendChild(c). Mamy więc idealną kontrolę nad tym kiedy skrypt się ściągnie. Co więcej, dodanie kilku skyptów na raz spowoduje, że będą się ściągać równocześnie. Super! Sposób ten działa zarówno w IE, FF, Operze jak i przeglądarkach opartych na WebKit (Chrome, Safari).
Połączmy teraz wszystkie elementy w jedną całość. Efekt można zobaczyć samemu albo na poniższym obrazku:

I oczywiście cały kod script-loader.js (polecam też zajrzeć w źródło rozwiązania):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function load_script(e) { var p=document.getElementsByTagName("HEAD")[0]; var c=document.createElement("script"); c.type="text/javascript"; c.src=e; p.appendChild(c); } function load_all() { load_script('script1.js.php'); load_script('script2.js.php'); load_script('script3.js.php'); } $(document).ready(function() { load_all(); }); |
Podsumowując: ładujemy wszystkie skrypty dynamicznie po załadowaniu struktury DOM
Rozwiązanie ma następujące efekty:
- nie widać znaczących przestojów w czasie ładowania strony, pomimo użycia skryptów wprowadzających spore opóźnienie (nie liczę tu czasu wczytania pomocniczej biblioteki jQuery)
- skrypty nie blokują wczytywania innych elementów strony. W przykładowym rozwiązaniu do czasu ich uruchomienia cała strona zostanie już wyświetlona
- skrypty ładują się równolegle, bez konieczności czekania na siebie
Ma niestety również pewną wadę: co z sytuacją kiedy różne skrypty są zależne od innych? Tu oczywiście rozwiązań jest wiele, być może kiedyś o tym napiszę.
Zainteresowanych poznaniem innych szczegółów optymalizacji odsyłam również do prezentacji “Even Faster Web Sites” Steve’a Soudersa.
Real World Scenarios
A teraz dwa przykłady znalezione w sieci:
1. Strona główna portalu gazeta.pl

Skrypt umieszczający reklamy wydłuża ładowanie strony o 1.34 s (w tym przypadku).
2. Blog Harnira

Klasyczne schodki: tracimy około 1 s na ładowanie pojedynczych skryptów z bieżącego template’u wordpressa.
No Comments yet »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Powered by WordPress with Hiperminimalist Theme design by Borja Fernandez.
Entries and comments feeds.
Valid XHTML and CSS.



