【问题标题】:Mediate and share data between different modules在不同模块之间调解和共享数据
【发布时间】:2016-03-23 22:05:43
【问题描述】:

我只是想了解事件驱动的 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

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

【问题讨论】:

  • 似乎你的 init 应该在任何更改后触发,在这种情况下它应该被称为渲染。看看 redux 模式,它基本上是一个像你一样的事件发射器,带有一个重新呈现状态的通配符事件。
  • 但是这样我似乎最终得到了一个必须明确知道所有模块实例的中介不,你不知道。你为什么这么认为? subscribe 函数应该在模块的上下文中调用。而所有 pubsub 需要知道的是调用它给出的回调。

标签: javascript events publish-subscribe event-driven


【解决方案1】:

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


如果你想要松耦合,pub-sub 是一个不错的选择。

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

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

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

因此,您更有可能拥有dataLoaderModule,而不是dataModule,它会在他完成加载后发布数据模型(例如{data: 3})。

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

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

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

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


假设如下:

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

我们会:

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),而是需要(或注入)我们的依赖项。


您可能会问:“如果我的意思是让我的配置从 UI 更改呢?”,让我们也看看:

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

另外,在更真实的应用中,UI 模块通常会包含一个模型,所以我们也这样做。

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

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

就是这样。

希望对你有帮助:)

如果没有 - 评论!

【讨论】:

  • 这太棒了,正是我想要的!谢谢你的帮助:)
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多