2016-03-23 5 views
8

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

У меня есть DataModule, что только обнажает некоторые данные:

var dataModule = { data: 3 }; 

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

var configModule = { factor: 2 }; 

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

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

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

pubsub.subscribe("init", function() { 
    displayModule.display(dataModule.data, configModule.factor); 
}); 
pubsub.publish("init"); // output: 6 

Однако этот путь я, кажется, в конечном итоге с посредником, который должен знать все модулей-экземпляров явно - есть ли даже способ избежать этого? Также я не знаю, как это будет работать, если есть несколько экземпляров этих модулей. Каков наилучший способ избежать глобальных переменных экземпляра? Думаю, мой вопрос в том, что было бы самым гибким способом управления чем-то подобным? Я на правильном пути, или это совершенно неправильно? Извините за то, что я не очень точен с моим вопросом, мне просто нужен кто-то, чтобы подтолкнуть меня в правильном направлении.

+1

Кажется, что ваш init должен срабатывать после любых изменений, и в этом случае его следует называть рендерингом. загляните в шаблон redux, это, в основном, эмитент событий, как и у вас, с подстановочным событием, которое повторно отображает состояние. – dandavis

+1

_However таким образом я, кажется, в конечном итоге с посредником, который должен знать все экземпляры модулей явно. Нет, вы этого не делаете. Почему вы так думаете? Функция подписки должна вызываться из контекста модуля. И все, что нужно знать pubsub, - это вызвать обратный вызов, который он задает. – sahbeewah

ответ

1

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


Это вы хотите потерять сцепление, паб-саб хороший способ идти.

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

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

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

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

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

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

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

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


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

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

Мы имеем:

данных-loader.js (ака dataLoaderModule)

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

configuration.js (ака configModule)

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

display.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), вместо этого мы требуем (или вводим) наши зависимости.


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

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

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

И позволяет также называть их "мнения" вместо "displayModules", и мы будем иметь:

данных-loader.js (ака dataLoaderModule)

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

настройки -view.js (aka settingsDisplayModule, aka config)

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); 
     } 
    }; 
}); 

данных-view.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(); 
    }); 
}); 

И это все.

Надеется, что это помогает :)

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

+0

Это потрясающе, именно то, что я искал! Спасибо за помощь :) – subarachnid

1

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

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