Понятие модуля

Сложный

Концепция AMD 

Front-end часть приложения Creatio представляет собой набор блоков функциональности, каждый из которых реализован в отдельном модуле. Согласно концепции Asynchronous Module Definition (AMD), в процессе работы приложения выполняется асинхронная загрузка модулей и их зависимостей. Таким образом, концепция AMD позволяет подгружать только те данные, которые необходимы для работы в текущий момент. О концепции AMD читайте на Википедии.

Концепция AMD поддерживается различными JavaScript-фреймворками. В Creatio для работы с модулями используется загрузчик RequireJS. Подробнее читайте на официальном сайте RequireJS.

Модульная разработка в Creatio 

Модуль — фрагмент кода, инкапсулированный в обособленный блок, который самостоятельно загружается и выполняется.

Создание модулей в специфике JavaScript декларируется паттерном программирования "Модуль". Подробнее читайте в статье JavaScript Module Pattern: In-Depth. Классическая реализация паттерна — использование анонимных функций, возвращающих определенное значение (объект, функцию и т. д.), которое ассоциируется с модулем. При этом значение модуля экспортируется в глобальный объект.

Пример экспорта значения модуля в глобальный объект
/* Немедленно вызываемое функциональное выражение (IIFE). Aнонимная функция, которая инициализирует свойство myGlobalModule глобального объекта функцией, возвращающей значение модуля. Таким образом, фактически происходит загрузка модуля, к которому в дальнейшем можно обращаться через глобальное свойство myGlobalModule. */
(function () {
    /* Обращение к некоторому модулю, от которого зависит текущий модуль.
    Загрузите модуль в глобальную переменную SomeModuleDependency до обращения к нему.
    Контекcт this — глобальный объект. */
    var moduleDependency = this.SomeModuleDependency;
    /* В свойстве глобального объекта объявляет функцию, которая возвращает значение модуля. */
    this.myGlobalModule = function () { return someModuleValue; };
}());

Интерпретатор, обнаруживая в коде такое функциональное выражение, сразу вычисляет его. В результате выполнения в свойство myGlobalModule глобального объекта помещается функция, которая возвращает значение модуля.

Особенности подхода:

  • Cложность декларирования и использования модулей–зависимостей.
  • В момент выполнения анонимной функции все зависимости модуля должны быть загружены.
  • Загрузка модулей–зависимостей выполняется в заголовке страницы через HTML-элемент <script>. Обращение к модулям–зависимостям осуществляется через имена глобальных переменных. При этом разработчику необходимо реализовывать порядок загрузки модулей–зависимостей.
  • Как следствие предыдущего пункта — модули загружаются до начала рендеринга страницы, поэтому в модулях невозможно обращаться к элементам управления страницы для реализации пользовательской логики.

Особенности использования подхода в Creatio:

  • Отсутствие возможности динамической загрузки модулей.
  • Применение дополнительной логики при загрузке модулей.
  • Сложность управления большим количеством модулей со многими зависимостями, которые могут перекрывать друг друга.

Загрузчик RequireJS 

Загрузчик RequireJS предоставляет механизм объявления и загрузки модулей, который базируется на концепции AMD, и позволяет учесть особенности, которые описаны выше.

Принципы работы механизма загрузчика RequireJS:

  • Объявление модуля выполняется в функции define(), которая регистрирует функцию–фабрику для инстанцирования модуля, но при этом не загружает его в момент вызова. Подробнее о функции define() читайте на сайте GitHub.
  • Зависимости модуля передаются как массив строковых значений, а не через свойства глобального объекта.
  • Загрузчик выполняет загрузку модулей–зависимостей, которые переданы в качестве аргументов функции define(). Модули загружаются асинхронно. Порядок загрузки произвольный и определяется загрузчиком.
  • После загрузки указанных зависимостей модуля вызывается функция–фабрика, которая возвращает значение модуля. При этом в нее в качестве аргументов передаются загруженные модули–зависимости.
Пример объявления модуля
Основы
Пример использования функции define() для объявления модуля SumModule
/* Модуль с именем SumModule реализует функциональность суммирования двух чисел.
Модуль не имеет зависимостей. Поэтому в качестве второго аргумента передается пустой массив, а анонимной функции-фабрике не передаются никакие параметры. */
define("SumModule", [], function () {
    /* Тело анонимной функции содержит внутреннюю реализацию функциональности модуля. */
    var calculate = function (a, b) { return a + b; };
    /* Возвращаемое функцией значение — объект, которым является модуль для приложения. */
    return {
        /* Описание объекта. В данном случае, модуль представляет собой объект со свойством summ. Значение этого свойства — функция с двумя аргументами, которая возвращает сумму этих аргументов. */
        summ: calculate
    };
});
Функция define()
Основы

Назначение функции define() — объявление асинхронного модуля в исходном коде. Загрузчик работает с этим модулем.

Объявление модуля
define(
    ModuleName,
    [dependencies],
    function (dependencies) {
    }
);

Параметры 

ModuleName

Строка с именем модуля. Необязательный параметр.

Если не указать параметр, загрузчик самостоятельно присваивает модулю имя в зависимости от его расположения в дереве скриптов приложения. Имя необходимо для обращения к модулю из других частей приложения (в том числе, для асинхронной загрузки в качестве зависимости другого модуля).

dependencies

Массив имен модулей, от которых зависит текущий модуль. Необязательный параметр.

Загрузчик RequireJS выполняет асинхронную загрузку модулей–зависимостей, которые переданы в качестве аргументов функции define(). Порядок зависимостей в массиве dependencies соответствует порядку перечисления параметров, которые передаются в фабричную функцию. Фабричная функция вызывается после загрузки зависимостей, которые перечислены в массиве dependencies.

function(dependencies)

Анонимная фабричная функция, которая инстанцирует модуль. Обязательный параметр.

Объекты, которые ассоциируются загрузчиком с модулями–зависимостями, передаются в качестве параметров функции. Зависимости перечисляются в массиве dependencies. Через аргументы массива выполняется доступ к свойствам и методам модулей–зависимостей внутри текущего модуля. Порядок зависимостей в массиве dependencies соответствует порядку перечисления параметров, которые передаются в фабричную функцию.

Фабричная функция возвращает значение, которое загрузчик ассоциирует как экспортируемое значение текущего модуля.

Виды возвращаемого значения фабричной функции:

  • Объект, которым является модуль для приложения. Модуль сохраняется в кэше браузера после первичной загрузки на клиент. Если объявление модуля изменено после загрузки на клиент (например, в процессе реализации конфигурационной логики), то очистите кэш и заново загрузите модуль.
  • Функция-конструктор модуля. В качестве аргумента в конструктор передается объект контекста, в котором создается модуль. Загрузка модуля приведет к созданию на клиенте экземпляра модуля (инстанцируемого модуля). Повторная загрузка модуля на клиент функцией require() приведет к созданию еще одного экземпляра модуля. Эти экземпляры одного и того же модуля приложение воспринимает, как самостоятельные модули. Примером объявления инстанцируемого модуля является модуль CardModule пакета NUI.