RequireJSПри разработке приложений с модульной структурой на JavaScript возникает две проблемы:
  • описание и удовлетворение зависимостей различных частей приложения, необходимость организации подключения зависимостей на серверной стороне;
  • экспорт переменных в глобальную область видимости и их коллизия.

Обе эти задачи решаются при использовании подхода Asynchronous Module Definition. Он сводится к описанию модулей функцией define и подключению их с помощью require. На данный момент есть несколько инструментов, реализующих AMD. Я начал своё знакомство с ними с RequireJS и был удивлён, насколько удобно и просто можно описывать зависимости модулей. Расскажу, как это работает, на простом примере.

Подключение загрузчика

Имеем следующую структуру каталогов:
siteroot/
  js/
    app.js
    require.js
    jquery.js
    mymodule.js
  index.html

Для начала, подключим в index.html загрузчик. Будем использовать RequireJS:
<script data-main="/js/app" src="/js/require.js"></script>

Отлично, это единственный тег script, который нам нужен. Остальную работу по подключению JS сделает загрузчик. Указанный в data-атрибуте файл (расширение .js для краткости в RequireJS всегда опускается) будет своеобразной точкой входа нашего приложения. В нём мы сможем подключить необходимые модули с помощью require и совершить задуманные действия.

Описание модуля

Опишем наш модуль в /js/module.js с помощью define:
define(
    'mymodule',
    ['jquery'],
    function( $ ){
        return {
            foo : 'bar'
        };
    }
);

Первый аргумент — строка, название модуля, не обязателен. Вторым аргументом передаются зависимости в виде массива строк, также опционально. Третий аргумент — функция-фабрика, которая выполняется только после удовлетворения всех зависимостей (загрузки перечисленных файлов). В неё в качестве аргументов передаются экспортируемые зависимостями переменные. А возвращать она должна сам модуль. В данном случае это объект с одним полем.

Использование

В /js/app.js подключим нужные модули с помощью JS и выполним свой код:
require(
    ['mymodule', 'jquery'],
    function( Module, $ ){
        $('body').append( Module.foo );
    }
);

Module при этом не будет доступна в глобальной области видимости, как и другие переменные, экспортируемые библиотеками из зависимостей. Не смотря на то, что библиотека jQuery с версии 1.7 поддерживает AMD-архитектуру, она является исключением: экспортирует свой доллар в глобальную область видимости. Скорее всего, это сделано для сохранения совместимости с армией плагинов, написанных за многие годы.

Конфигурация

RequireJS обладает рядом параметров, которые можно передавать перед использованием. Для этого служит объект require.config.

Что делать, если вам необходимо подключить мод��ль, которая не оформлен в виде AMD и экспортирует переменную в глобальную область видимости? Можно, конечно, модифицировать его исходный код, но это плохая практика. Для описания таких модулей служит параметр shim. Можно вручную указать его зависимости и экспортируемую переменную, и он станет частью нашего приложения наравне с другими AMD-парнями:
require.config = {
    shim: {
        'oldmodule' : {
            deps: [],
            exports: 'OldModule'
        }
    }
};

Теперь можно указывать его в качестве зависимости:
require(
    ['mymodule', 'jquery', 'oldmodule'],
    function(){}
);

Помимо shim есть ещё много параметров: корневая директория подключения файлов baseUrl, псевдонимы для более удобного подключения paths, и т.д.

Заключение

Надеюсь, концепция AMD зацепила вас, так же, как и меня. Она помогает избежать хаоса при использовании большого количества JS-файлов в разработке, подталкивает к написанию реюзабельного кода, снимает ответственность за подключение файлов с бэкенда. А если у вас реально большое MVC-приложение из пары десятков файлов, то подобная система просто незаменима.

На прощание, приведу несколько ссылок, которые помогут продолжить изучение вопроса:

Исходный код из статьи доступен в репозитории на GitHub.
Happy hacking!