Jednym ze sposobów na przyspieszenie wczytywania strony internetowej jest asynchroniczne ładowanie skryptów JavaScript. Jest to ważne szczególnie kiedy wykorzystujemy na naszych stronach skrypty znajdujące się na zdalnych serwerach, ponieważ często ich pobieranie trwa długo. Przeglądarki internetowe, domyślnie podczas wczytywania strony przetwarzają poszczególne elementy DOM jeden po drugim czyli synchronicznie. Jako, że script również jest takim elementem, jego przedłużone wczytywanie może zatrzymać przetwarzanie pozostałych elementów. Na szczęście istnieją sposoby, żeby zmusić przeglądarkę do ładowania skryptów JavaScript asynchronicznie i tym tematem zajmę się w dzisiejszym wpisie. Zanim jednak do tego przejdziemy, spójrzmy na przykład, który posłuży nam jako punkt wyjścia do dalszych rozważań:

<!DOCTYPE html>
<html>
  <head>
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
  </head>
  <body>
     <h1>Asynchroniczne ładowanie skryptów JavaScript</h1>
     <img src="http://devget.net/wp-content/uploads/2014/05/html-css-js.png">
   </body>
</html>

W powyższymdokumencie HTML, w jego sekcji head widzimy standardowy sposób na deklarację skryptu JavaScript - pobieramy w tym przypadku bibliotekę jQuery, znajdującą się na zdalnym serwerze. Następnie w sekcji body znajduje się obrazek, który również pobierany jest ze zdalnego serwera.

Jeśli wczytamy taki dokument HTML i podejrzymy ruch sieciowy podczas ładowania strony za pomocą wtyczki Firebug **możemy stwierdzić, że rzeczywiście obrazek wczytywany jest dopiero kiedy zakończy się pobieranie pliku **JavaScript:

firebug przykład pierwszy

Jak więc spowodować aby przeglądarka nie czekała na pobranie skryptu JS ze zdalnego serwera? O tym w kolejnych akapitach.

Podejście old school’owe

Na początek przyjrzymy się podejściu, które stosowane było zanim przeglądarki internetowe zaczęły porządnie wspierać standardy HTML5. Wzorzec ten nazywany jest wstrzykiwaniem skryptu i… jest nim dosłownie. Zresztą sami zobaczcie:

<!DOCTYPE html>
<html>
  <head>
    <script>
      var script = document.createElement('script');
      script.src = "http://code.jquery.com/jquery-1.11.1.min.js";
      document.getElementsByTagName('head')[0].appendChild(script);
    </script>
  </head>
  <body>
    <h1>Asynchroniczne ładowanie skryptów JavaScript</h1>
    <img src="http://devget.net/wp-content/uploads/2014/05/html-css-js.png">
  </body>
</html>

Jak widzicie, tym razem w sekcji head mamy deklarację skryptu “inline”. Nie robi on nic innego jak tworzy element script, a następnie wstrzykuje go do elementu head… czyli w zasadzie poprzedni przykład tylko “na około”. Dzieje się tak ponieważ, skrypty tworzone i dodawane dynamicznie do strony internetowej są domyślnie asynchroniczne - nie blokują one ładowania pozostałych elementów strony, zresztą poniżej dowód:

firebug przykład drugi

Wzorzec ten nie jest wolny od wad. Jako, że przeglądarka nie wie co dokładnie wydarzy się w skrypcie “inline”, musi zablokować ładowanie stylów CSS do czasu zakończenia wykonywania skryptu (to dlatego, że JavaScript ma możliwość manipulowania stylami), warto więc taką sekcję umieszczać przed deklaracją stylów CSS.

Opisane rozwiązanie jest już obecnie przestarzałe ponieważ, praktycznie wszystkie nowoczesne przeglądarki implementują rozwiązanie przewidziane w specyfikacji HTML5. O tym w kolejnym punkcie.

Asynchroniczne ładowanie skryptów - podejście z HTML5

Tak jak wspomniałem, w HTML5 asynchroniczne przetwarzanie skryptów dostępne jest standardowo i można je uzyskać bardzo prosto - wystarczy skorzystać z atrybutu async:

<!DOCTYPE html>
<html>
  <head>
    <script async src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
  </head>
  <body>
    <h1>Asynchroniczne ładowanie skryptów JavaScript</h1>
    <img src="http://devget.net/wp-content/uploads/2014/05/html-css-js.png">
  </body>
</html>

Pod tym linkiem możecie sprawdzić kompatybilność tego rozwiązania przeglądarkami - jak widzicie, obecnie tylko Opera Mini nie potrafi obsłużyć tego atrybutu…

Problemem w opisanym podejściu może być fakt, że wykorzystując atrybut async nie mamy gwarancji kolejności wykonania skryptów. Jeśli więc między wczytywanymi plikami istnieją zależności, możliwe że będziemy musieli rozważyć użycie biblioteki takiej jak require.js do ładowania skryptów… (istnieje niby atrybut defer, jednak jest on słabo obsługiwany przez przeglądarki i nie zaleca się jego wykorzystania).

Dla porządku jeszcze dowód na to, że atrybut async również działa:

firebug przykład trzeci

Podsumowanie

Oczywiście, opisane sposoby nie są jedynymi na uzyskanie pożądanego efektu. Powstało mnóstwo bibliotek/wtyczek ułatwiających asynchroniczne wczytywanie skryptów (na przykład script.js), również w jQuery istnieje odpowiednia metoda ($.getScript). Moim zamierzeniem było raczej zwrócenie uwagi na asynchroniczne ładowanie skryptów za pomocą sposobu dostępnego w HTML5 i jego zalety i wady.