在不同模块之间调解和共享数据

时间:2016-03-23 22:05:43

标签: javascript events publish-subscribe event-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 个答案:

答案 0 :(得分:1)

您不需要调解员。只需导入数据,配置和显示,然后在需要的地方拨打display(data, config)

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

答案 1 :(得分:1)

你走在正确的轨道上,我会尽力给你额外的推动:

你想要松耦合,pub-sub是一个很好的方法。

但是,你真的不需要那个“中介”,理想情况下每个模块应该是自治的并封装自己的逻辑。

这是通过以下方式完成的:每个模块依赖于pubsub服务,订阅所有相关事件并对其进行操作。每个模块还发布可能与其他人相关的事件(一分钟内的代码样本,请耐心等待。)

我认为你可能在这里缺少的是那些使用事件的模块,几乎不会只是简单的模型。他们将有一些逻辑,并且持有一个模型(他们在接收事件时更新)。

因此,一旦他完成加载,您就更有可能拥有dataModule来发布数据模型(例如dataLoaderModule),而不是{data: 3}

您设置的另一个重要要求是在避免全局实例变量的同时共享数据 - 这是一个非常重要的概念,也是朝着正确方向迈出的一步。您在此解决方案中错过的是 - 依赖注入或至少是一个允许定义依赖关系的模块系统。

你知道,拥有一个事件驱动的应用程序并不一定意味着代码的每一部分都应该使用事件进行通信。应用程序配置模型或实用程序服务绝对是我要注入的(当使用DI时,如Angular中),require(使用AMD / CommonJS时)或导入(使用ES6模块时)。
(即,然后使用事件与实用程序通信)。

在您的示例中,不清楚configModule是静态应用配置还是我可以从UI调整的旋钮。如果它是一个静态的应用程序配置 - 我会注入它。

现在,让我们看一些例子:

假设如下:

  • 而不是dataModule我们有dataLoaderModule
  • configModule是一个静态configuration模型。
  • 我们正在使用AMD模块(而不是我喜欢的ES6模块),因为我发现你只使用ES5功能(我看不到任何类或功能)。

我们会:

data-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(遵循您的命名惯例)。

此外,在更逼真的应用程序中,UI模块通常会包含模型,所以我们也可以这样做。

并且还可以将它们称为“视图”而不是“displayModules”,我们将:

data-loader.js (又名dataLoaderModule)

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

settings-view.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);
        }
    };
});

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

就是这样。

希望有所帮助:)

如果没有 - 评论!