Передача и обмен данными между различными модулями
Я просто пытаюсь получить мою голову вокруг события 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)
}