Передача и обмен данными между различными модулями

Я просто пытаюсь получить мою голову вокруг события driven JS, так что, пожалуйста, медведь со мной. В моем приложении есть различные типы модулей. Некоторые просто инкапсулируют данные, другие управляют частью DOM. Некоторые модули зависят от других, иногда один модуль зависит от состояния нескольких других модулей, но я не хочу, чтобы они общались напрямую или передавали один модуль другому только для легкого доступа. Я попытался создать простой сценарий, чтобы проиллюстрировать мою проблему (фактический модули, конечно, намного сложнее):

у меня есть dataModule, который просто предоставляет некоторые данные:

var dataModule = { data: 3 };

существует configModule, который предоставляет модификаторы для отображения этих данных:

var configModule = { factor: 2 };

наконец, есть displayModule, который объединяет и отображает данные из двух других модулей:

var displayModule = {
  display: function(data, factor) {
    console.log(data * factor);
  }
};

у меня также есть простая реализация pub-sub, поэтому я мог бы просто посредничать между модулями, как это:

pubsub.subscribe("init", function() {
  displayModule.display(dataModule.data, configModule.factor);
});
pubsub.publish("init"); // output: 6
таким образом, я, похоже, получаю посредника, который должен знать все экземпляры модуля явно-есть ли способ избежать этого? Также я не знаю, как это будет работать, если есть несколько экземпляров этих модулей. Каков наилучший способ избежать глобальных переменных экземпляра? Я думаю, мой вопрос в том, что было бы самым гибким способом управлять чем-то подобным? Я на правильном пути, или это совсем неправильно? Извините за не очень точный вопрос, мне просто нужно, чтобы кто-то подтолкнуть меня в правильном направлении.

2 ответов


вы на правильном пути, я постараюсь дать вам дополнительный толчок, о котором вы говорите:


это вы хотите свободную муфту, паб-суб хороший способ пойти.

но вам действительно не нужен этот "посредник", каждый модуль должен быть идеально автономным и инкапсулировать свою собственную логику.

это делается следующим образом: каждый модуль зависит от сервиса pubsub, подписывается на все соответствующие события и действует по ним. Каждый модуль также публикует события, которые могут иметь отношение к другим (примеры кода через минуту, несут меня).

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

так вместо dataModule у вас, скорее всего, будет dataLoaderModule который опубликует модель данных (например,{data: 3}), как только он закончит погрузка.

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

вы видите, что наличие приложения, управляемого событиями, не обязательно означает, что каждая часть кода должны общаться с помощью событий. - модель конфигурации приложения или служебная служба-это определенно то, что я бы ввел (при использовании DI, как в Angular), требуется (при использовании AMD/CommonJS) или импорт (при использовании модулей ES6).
(т. е. вместо того, чтобы общаться с утилитой, используя события).

в вашем примере неясно, будет ли configModule это статическая конфигурация приложения или какая-то ручка, которую я могу настроить из пользовательского интерфейса. Если это статическая конфигурация приложения - я бы ее ввел.

теперь, давайте посмотрим некоторые примеры:


предполагая следующее:

  • вместо dataModule у нас есть dataLoaderModule
  • configModule статический configuration модель.
  • мы используем модули AMD (а не модули ES6, которые я предпочитаю), так как я вижу, что вы придерживаетесь использования только функций ES5 (я не вижу классов или const).

мы имели бы следующее:

сведения-погрузчик.js (ака dataLoaderModule)

define(['pubsub'], function (pubsub) {
    // ... load data using some logic...
    // and publish it
    pubsub.publish('data-loaded', {data: 3});
});

конфигурации.js (он же configModule)

define([], function () {
    return {factor: 2};
});

дисплей.js (он же displayModule)

define(['configuration', 'pubsub'], function (configuration, pubsub) {
    var displayModule = {
        display: function (data, factor) {
            console.log(data * factor);
        }
    };

    pubsub.subscribe('data-loaded', function (data) {
        displayModule.display(data, configuration.factor);
    });
});

вот и все.

вы заметите, что у нас здесь нет глобальных переменных (даже pubsub), вместо этого мы требуем (или вводим) наши зависимости.


здесь вы можете спросить:"а что, если я имел в виду для меня config для изменения из пользовательского интерфейса?", так что давайте посмотрим, что тоже:

в этом случае, я скорее переименовать configModule to settingsDisplayModule (следуя вашему соглашению об именах).

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

и давайте также назовем их "views" вместо "displayModules", и у нас будет:

сведения-погрузчик.js (ака dataLoaderModule)

define(['pubsub'], function (pubsub) {
    // ... load data using some logic...
    // and publish it
    pubsub.publish('data-loaded', {data: 3});
});

настройки-вид.js (settingsDisplayModule ака конфиг)

define(['pubsub'], function (pubsub) {
    var settingsModel = {factor: 2};

    var settingsView = {
        display: function () {
            console.log(settingsModel);

            // and when settings (aka config) changes due to user interaction,
            // we publish the new settings ...
            pubsub.publish('setting-changed', settingsModel);
        }
    };
});

сведения-вид.js (он же displayModule)

define(['pubsub'], function (pubsub) {
    var model = {
        data: null,
        factor: 0
    };

    var view = {
        display: function () {
            if (model.data && model.factor) {
                console.log(model.data * model.factor);
            } else {
               // whatever you do/show when you don't have data
            }
        }
    };

    pubsub.subscribe('data-loaded', function (data) {
        model.data = data;
        view.display();
    });

    pubsub.subscribe('setting-changed', function (settings) {
        model.factor = settings.factor;
        view.display();
    });
});

и это все.

надеюсь, что это помогает :)

если нет - комментарий!


вам не нужен посредник. Просто импортируйте данные, конфигурацию, отображение и вызов display(data, config) где нужно.

// import data
// import config
function render(){
    display(data, config)
}