Blog webdeveloperski Patryk yarpo Jar

Require.js – AMD w praktyce

Autor wiadomości Grudzień 16, 2012

Już kilkukrotnie poruszałem temat tworzenia modułów (czasem nawet "klas") w JavaScripcie. Tym razem coś nowego - asynchroniczne definiowanie modułów. W skrócie AMD (ang. asynchronous module definition). Coraz więcej znanych frameworków i bibliotek wykorzystuje właśnie to podejście (choćby Dojo i jQuery).

W tym wpisie spróbuję pokazać dlaczego powstało takie rozwiązanie oraz przedstawić kilka prostych zastosowań.

Na początek warto:

  • mieć jakieś podstawy JavaScript
  • zarezerwować 5 minut

Za wiele <script /> w kodzie?

Czy tworząc bogate aplikacje internetowe oparte o JavaScript nie masz czasem przytłaczającego wrażenia, że połowa kodu HTML to zaczynają być znaczniki załączające kolejne pliki `*.js'? Czy taki widok jest dla Ciebie codziennością:

<html>
<body>
   <script src="jquery.js"></script>
   <script src="1.js"></script>
   <script src="http://example.com/a.js"></script>
   ...
   <script src="z.js"></script>
</body></html>

W przypadku takiej strony, im jest ona bardziej rozbudowana, tym więcej plików trzeba załączyć. Jeśli mamy wiele podstron sytuacja powtarza się na wielu z nich. Dodatkowo nie było możliwości określenia zależności między konkretnymi "modułami" (plikami, które często były całymi frameworkami czy bibliotekami).

Jak sobie z tym radziliśmy?

Kilka razy pokazywałem już jak można zmniejszyć liczbę odwołań do serwera i rozmiar plików JS, oto moje autorskie rozwiązanie:

<html>
<head>
    <script src="script.php?files=file_1,file_2" type="text/javascript"></script>
</head>
<body></body></html>

Dokładny opis, jak miałoby to działać znajdziesz w tych wpisach (wiedza ta nie jest niezbędna do zrozumienia tego wpisu):

Nadal jednak trzeba było ładować skrypty "globalnie". Nie było możliwości, aby konkretny załączony plik js określił jakich innych plików potrzebuje. Wymaganie, o którym ciągle wspominam brzmi wręcz jak mechanizmy `include', `using' czy `import' z języków uznawanych powszechnie za bardziej dojrzałe (C, C#, Java, itp.). Czy podobny mechanizm w języku takim jak JavaScript jest osiągalny?

Nowe podejście - AMD

Powyższe przykłady zaprowadziły nas do nowego rozwiązania, które musiałby pozwalać:

  1. tworzyć moduły zawarte w pojedynczych plikach,
  2. załączać (w automatyczny sposób) te pliki odczytując w jakiś sposób ich wzajemne zależności "lokalnie" (moduł sam mówi czego jeszcze potrzebuje),
  3. rozbić wielkie biblioteki na mniejsze fragmenty, tak aby ładować tylko to, czego naprawdę potrzebujemy,
  4. móc korzystać z modułów zawartych w różnych bibliotekach bez ładowania całych bibliotek (obsługa grafiki z biblioteki X, obsługa zdarzeń z ABC).

Brzmi to pięknie. Czy da się to jednak osiągnąć w JavaScript? Może w nowszych wersjach, bo przecież teraz nie ma jeszcze odpowiednich mechanizmów w tym języku - pewnie pomyślało wielu. A jednak. Można. Dzisiejsze mechanizmy wystarczą.

AMD - przykład wykorzystania

W przykładach wykorzystam darmową bibliotekę Require.js.

<!DOCTYPE html>
<html>
    <head>
        <title>Require.js - Przykład wykorzystania AMD [yarpo.pl]</title>
        <meta charset="utf-8" />
        <script src="require.js"></script>
        <script>
            require(['modules/HelloWorldAlerter'], function(HelloWorldAlerter)
            {
                var alerter = new HelloWorldAlerter();
                alerter.run();
            });
        </script>
    </head>
    <body></body></html>

demo online
pobierz działający kod (należy uruchamiać na serwerze WWW)

W powyższym przykładzie wykorzystałem funkcję `require', która została zaimplementowana wewnątrz biblioteki `Require.js'. Przyjmuje ona 2 argumenty:

  1. tablica modułów, jakie będą wykorzystywane
  2. funkcję, która ma zostać wykonana, gdy wszystkie moduły zostaną załadowane

Być może zauważyłeś, że przekazana funkcja posiada w tym wypadku jeden argument - `HelloWorldAlerter'. Jest to konstruktor modułu. Gdyby podano więcej modułów, liczba parametrów funkcji także zwiększałaby się:

require(['modules/A', 'modules/B', 'widgets/AXT', 'Utils'], function(modulA, bModule, AXT, InnaNazwaBoNazwaToTylkoNazwa)
{
    var objectA = new modulA()
    ,   objectB = new bModule()
    ,   oWidget = new AXT()
    ,   oUtils = new InnaNazwaBoNazwaToTylkoNazwa()
    ,   oUtils2 = new arguments[3](2); // za pomocą obiektu `arguments' oraz z przekazaniem wartości do konstruktora

    objectA.run();
    objectB.run();
    oWidget.create();
    oUtils.run();
    oUtils2.run();
});

demo online
kod

Zwróć uwagę, że parametry funkcji mogą mieć dowolną nazwę. To Ty decydujesz jak lokalnie - w tej funkcji będą się nazywać ładowane moduły (można nawet odwoływać się do nich za pomocą obiektu `arguments'). Polecam jednak stosowanie pewnej stałej konwencji.

Jak tworzyć własne moduły?

Temat ten poruszyłem w osobnym wpisie dotyczącym tworzenia modułów JavaScript za pomocą AMD.

Jeśli chciałbyś zgłębić wiedzę na ten temat polecam:

  • zajrzeć w kod przykładów on-line dołączonych do tego posta (bądź pobrać kody źródłowe [1],[2])
  • poczytać na stronach biblioteki Require.js (dokumentacja)
  • popatrzeć jak wygląda kod znanych bibliotek np. Dojo (kod z SVN)

Powodzenia 🙂

Komentarze (0) Trackbacks (0)

Brak komentarzy.


Leave a comment

 

Brak trackbacków.