search check home clock-o tag tags chevron-left chevron-right chevron-up chevron-down twitter facebook github rss comment comments terminal code

[JS] Jak tworzyć modułowy kod JS z wykorzystaniem require.js?

[JS] Jak tworzyć modułowy kod JS z wykorzystaniem require.js?

Tak jak obiecałem na fanpage'u bloga, w najbliższym czasie możesz się spodziewać większej ilości artykułów dotyczących języka JavaScript.
Dzisiejszy wpis będzie dotyczył tworzenia i korzystania z modułowego kodu JavaScript w aplikacjach internetowych.

Modułowość kodu JS

Na czym polega modułowość kodu JavaScript w aplikacji internetowej?
Polega ona na tym, aby tak tworzyć kod JavaScript, aby każda jego funkcjonalna część była niezależna od innych oraz aby możliwe było korzystanie z dowolnych modułów w zależności od potrzeb. Takie podejście pozwala na ograniczenie bałaganu w kodzie oraz na tworzenie wydajniejszych aplikacji webowych.

Kilka słów o AMD

AMD, w tym przypadku, to nie jest nazwa pdoducenta procesorów i kart graficznych. To jest akronim od Asynchronous Module Definition, czyli w wolnym tłumaczeniu - asynchronicznej definicji modułu. Jest to wzorzec tworzenia kodu w języku JavaScript, który pozwala na asynchroniczne, niezależne ładowanie modułów za pomocą mechanizmów ładowania kodu JavaScript (script loaders), takich jak require.js czy curl.js.

AMD

Wzorzec projektowy dla języka JavaScript, gdzie moduł i jego zależności mogą być ładowane asynchronicznie.

Tworzenie modułu zgodnego z AMD w JS

W związku z wykorzystywaniem możliwości jakie dają wzozec AMD oraz require.js bardzo często będziesz korzystał z:

  • define() - metody, która jest odpowiedzialna za utworzenie opakowania dla kodu modułu,
  • oraz require() - metody, która jest odpowiedzialna za ładowanie modułów.

Moduł we wzorcu AMD tworzymy za pomocą define(), ktory może przyjąć 3 parametry:

  • opcjonalnie, nazwę modułu,
  • opcjonalnie, tablicę zależnych modułów,
  • definicję funkcji, która inicjalizuje moduł.

Dobrą praktyką jest nazywanie modułu za pomocą ścieżki dostępu do pliku, np. app/modules/module. W ten sposób unikniemy problemów z podobnie nazwanymi modułami, co ma czasem miejsce.
Z kolei tablica zależnych modułów, jest to lista modułów, które są wymagane do tego, aby nasz zdefiniowany moduł działał poprawnie po załadowaniu na stronie.

Przykladowy kod, który przedstawia definicję modułu, może wyglądać następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//moduł znajduje się w pliku app/modules/module.js
define('app/modules/module', ['lib/jquery', 'lib/lodash'], function ($, _)) {
  var module = function () {
    var getName = function () {
      $('body').append('<h1>AMD Module</h1>';
    };
    var makeCalculations = function (type, items) {
      if (type === 'multiply') {
        items = _.map(items, function (item) {
          return item * 2;
        });
      }
      return items;
    }
    return {
      getName     : getName,
      multiplyByTwo   : function (items) {
        return makeCalculations('multiply', items);
      }
    };
  };
});

Jak widać, w powyższym kodzie zdefiniowano moduł o nazwie module, będący zależnym od bibliotek: jQuery oraz lodash, a który definiuje obiekt module wraz z jego metodami, które są dostępne z zewnątrz.
Dla niewtajemniczonych, taka postać tworzenia obiektów jest przykładem wzorca modułu z metodami dostępnymi publicznie, jak i prywatnymi.

Za pomocą define() możemy również tworzyć moduły, które zawierają w sobie inne moduły z ich wlasnymi zależnościami oraz moduły, nie potrzebują na starcie jakichkolwiek innych bibliotek, ale mogą ich potrzebować potem.
Przyklad tego drugiego przypadku wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//przykład ze strony: http://addyosmani.com/writing-modular-js/
define(function (require) {
  var isReady = false;
  var foobar;
 
  require(['foo', 'bar'], function (foo, bar) {
    isReady = true;
    foobar = foo() + bar();
  });
 
  return {
    isReady : isReady,
    foobar  : foobar
  };
});

Sposób użycia modułu w kodzie JS

Mając tak przygotowany moduł, możemy się do niego odwołać w następujący sposób:

1
2
3
4
require(['app/modules/module'], function (Module) {
  var moduleInstance = new Module();
  module.getName();
});

Prosto, łatwo i przyjemnie możemy korzystać z przygotowanych modułów. Dzięki czemu, nie musimy ładować wszystkich plików od razu na starcie aplikacji tylko w razie potrzeby.

Modułowy kod JS - podsumowanie

Tworząc modulowy kod JavaScrip możemy osiągnąć szereg korzyści, takich jak:

  • Zachowanie globalnej przestrzeni nazw czystej i wolnej od jakichkolwiek dodatkowych zmiennych,
  • Możliwość przenoszenia kodu między projektami, w końcu moduły są bytami niezależnymi (względnie),
  • Dodatkowe pliki, w których znajdują się moduły, nie muszą być ładowane od razu na starcie, lecz mogą być ładowane wedle potrzeby potem.

  • Comandeer

    require.js bez wątpienia jest najpopularniejszym loaderem AMD, ale nie jedynym. osobiście przestałem z niego korzystać z jednego, prostego powodu: miał problemy z obsługą modułów zoptymalizowanych przez własny optymizer r.js… dlatego przerzuciłem się na curl.js
    obecnie nie wyobrażam sobie pracy bez AMD. jest o wiele łatwiej i przyjemniej.
    btw AFAIR de facto tylko ścieżka jako nazwa modułu działa. gdy dołączamy plik ze ścieżki np /app/module.js a jako nazwę w define podamy np Modulasty, to loader powinien rzucić błąd o nieznalezionym module – czy może się mylę?