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

Mediate and share data between different modules

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

我有一个只公开一些数据的数据模块:

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

然而,这样我似乎最终得到一个必须明确知道所有模块实例的调解器 - 甚至有办法避免这种情况吗?我也不知道如果这些模块有多个实例,这将如何工作。避免全局实例变量的最佳方法是什么?我想我的问题是,管理此类事情的最灵活方式是什么?我走在正确的轨道上,还是完全错误?抱歉我的问题不是很准确,我只是需要有人把我推向正确的方向。

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

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

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


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

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

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

我认为您在这里可能遗漏的一点是,使用事件的模块绝不会只是普通模型。他们将有一些逻辑并且可以持有一个模型(他们在接收事件时更新)。

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

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

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

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

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


假设如下:

  • 我们有 dataLoaderModule
  • 而不是 dataModule
  • configModule 是静态 configuration 模型。
  • 我们正在使用 AMD 模块(而不是我更喜欢的 ES6 模块),因为我看到你坚持只使用 ES5 功能(我没有看到 类 或 consts)。

我们会:

data-loader.js(又名 dataLoaderModule)

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

configuration.js(又名配置模块)

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

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

就是这样。

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


这里你可能会问:"and what if I meant for my config to change from the UI?",我们也来看看:

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

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

我们也称它们为 "views" 而不是 "displayModules",我们将有:

data-loader.js(又名 dataLoaderModule)

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

设置-view.js(又名设置显示模块,又名配置)

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(又名显示模块)

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

就是这样。

希望对您有所帮助:)

如果没有 - 评论!