Модульный подход к разработке web-приложений с использованием JavaScript: AMD и RequireJS

в 21:03, , рубрики: Asynchronous Module Definition, best practices, javascript, 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' : {
            depts: [],
            exports: 'OldModule'
        }
    }
};

Теперь можно указывать его в качестве зависимости:

require(
    ['mymodule', 'jquery', 'oldmodule'],
    function(){}
);

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

Заключение

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

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

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

Автор: clslrns

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js