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

sub*_*nid 8 javascript events event-driven publish-subscribe

我只是试图让我的头脑围绕事件驱动的JS,所以请耐心等待我.我的应用程序中有不同类型的模块.有些只是封装数据,有些则管理DOM的一部分.有些模块依赖于其他模块,有时一个模块依赖于多个其他模块的状态,但我不希望它们直接通信或将一个模块传递给另一个模块以便于访问.我试图创建最简单的场景来说明我的问题(实际的模块当然要复杂得多):

我有一个dataModule只暴露一些数据:

var dataModule = { data: 3 };
Run Code Online (Sandbox Code Playgroud)

有一个configModule公开了用于显示该数据的修饰符:

var configModule = { factor: 2 };
Run Code Online (Sandbox Code Playgroud)

最后有一个displayModule,它结合并呈现来自其他两个模块的数据:

var displayModule = {
  display: function(data, factor) {
    console.log(data * factor);
  }
};
Run Code Online (Sandbox Code Playgroud)

我也有一个简单的pub-sub实现,所以我可以像这样在模块之间进行调解:

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

然而,这种方式我似乎最终得到了一个必须明确知道所有模块实例的中介 - 有没有办法避免这种情况?如果这些模块有多个实例,我也不知道这是如何工作的.避免全局实例变量的最佳方法是什么?我想我的问题是管理类似的东西最灵活的方法是什么?我是在正确的轨道上,还是这完全错了?很抱歉我的问题不是很准确,我只需要有人帮我推进正确的方向.

Yoa*_*oni 1

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


如果您想要松散耦合,发布-订阅是一个不错的选择。

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

这是通过以下方式完成的:每个模块都依赖于 pubsub 服务,订阅所有相关事件并对其采取行动。每个模块还发布可能与其他模块相关的事件(稍后会提供代码示例,请耐心等待)。

我认为您可能会忽略的一点是,使用事件的模块永远不会只是简单的模型。它们内部会有一些逻辑,并且可以保存一个模型(它们在接收事件时更新该模型)。

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

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

您会看到,拥有事件驱动的应用程序并不一定意味着每段代码都应该使用事件进行通信。应用程序配置模型或实用服务绝对是我会注入的东西(当使用 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});
});
Run Code Online (Sandbox Code Playgroud)

configuration.js(又名 configModule)

define([], function () {
    return {factor: 2};
});
Run Code Online (Sandbox Code Playgroud)

display.js(又名显示模块)

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);
    });
});
Run Code Online (Sandbox Code Playgroud)

就是这样。

您会注意到我们这里没有全局变量(甚至没有 pubsub),而是需要(或注入)我们的依赖项。


在这里您可能会问:“如果我的意思是从 UI 更改我的配置怎么办?” ,让我们也看看:

在这种情况下,我宁愿重命名configModulesettingsDisplayModule(遵循您的命名约定)。

另外,在更现实的应用程序中,UI 模块通常会保存一个模型,所以我们也这样做。

我们也将它们称为“视图”而不是“显示模块”,我们将有:

data-loader.js(又名 dataLoaderModule)

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

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);
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

data-view.js(又名显示模块)

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();
    });
});
Run Code Online (Sandbox Code Playgroud)

就是这样。

希望能帮助到你 :)

如果没有 - 评论!