Документация по разработке bpm’online
PDF
Документация по разработке
Описание платформы

Концепция AMD. Понятие модуля

Glossary Item Box

Общие положения

Начиная с версии 7.0, клиентская часть приложения bpm'online имеет модульную структуру, то есть реализована в виде набора блоков функциональности, каждый из которых реализован в отдельном модуле. В процессе работы приложения загрузка модулей и их зависимостей выполняется в соответствии с подходом Asynchronous Module Definition (AMD).

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

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

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

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

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

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

Основные недостатки такого подхода заключаются в сложности декларирования и использования модулей-зависимостей для таких модулей:

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

Таким образом, это, по сути, означает отсутствие возможностей динамической загрузки модулей, применения какой-либо дополнительной логики при загрузке модулей и т.д. В крупных проектах, каким является bpm'online, возникает дополнительная проблема в виде сложности управления большим количеством модулей со многими зависимостями, которые могут перекрывать друг друга.

Загрузчик RequireJS

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

  1. Объявление модуля выполняется в специальной функции define(), которая регистририрует функцию-фабрику для инстанцирования модуля, но при этом не загружает его немедленно в момент вызова.
  2. Зависимости модуля передаются как массив строковых значений, а не через свойства глобального объекта.
  3. Загрузчик выполняет загрузку всех модулей-зависимостей, переданных в качестве аргументов в define(). Модули загружаются асинхронно, при этом фактически порядок их загрузки определяется загрузчиком произвольно.
  4. После того как загрузчиком будут загружены все указанные зависимости модуля, будет вызвана функция-фабрика, которая вернет значение модуля. При этом в функцию-фабрику в качестве агрументов будут переданы загруженные модули-зависимости.

Объявление модуля. Функция define()

Для того чтобы загрузчик мог работать с асинхронным модулем, этот модуль должен быть объявлен в исходном коде с помощью функции define() следующим образом.

define(
        ModuleName,
        [dependencies],
        function (dependencies) {
        }
);

Параметры функции define() приведены в таблице 1.

Табл. 1. — Параметры функции define()

Аргумент Значение
ModuleName

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

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

dependencies

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

RequireJS выполняет загрузку всех зависимостей, переданных в массиве. Нужно учитывать, что порядок перечисления зависимостей в массиве dependencies должен соответствовать порядку перечисления параметров, передаваемых в функцию-фабрику. Функция-фабрика будет вызвана только после того, как будут загружены все зависимости, перечисленные в dependencies. Загрузка модулей-зависимостей выполняется асинхронно.

function(dependencies)

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

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

Функция-фабрика будет вызвана только после того, как будут загружены все модули-зависимости данного модуля (перечисленные в параметре dependencies).

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

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

Пример использования функции define() для объявления модуля SumModule, реализующего функциональность суммирования двух чисел.

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

Функция-фабрика возвращает в качестве значения модуля объект, которым модуль будет являться для системы.

© Terrasoft 2002-2019.

Был ли данный материал полезен?

Как можно улучшить эту статью?