我只是想了解事件驱动的 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
但是这样一来,我似乎最终得到一个必须明确知道所有模块实例的中介 - 甚至有办法避免这种情况吗?我也不知道如果这些模块有多个实例,这将如何工作。避免全局实例变量的最佳方法是什么?我想我的问题是,管理此类事情的最灵活方式是什么?我走在正确的轨道上,还是完全错误?抱歉我的问题不是很准确,我只需要有人将我推向正确的方向。
最佳答案
你走在正确的轨道上,我会尽力给你你所说的额外插入力:
如果您想要松散耦合,pub-sub 是一个不错的选择。
但是,您并不真的需要那个“中介”,理想情况下每个模块都应该是自治的并封装自己的逻辑。
这是通过以下方式完成的:每个模块都依赖于 pubsub 服务,订阅所有相关事件并根据它们采取行动。每个模块还发布可能与其他模块相关的事件(稍后会编写代码示例,请耐心等待)。
我认为您在这里可能遗漏的一点是,使用事件的模块绝不会只是普通模型。他们将有一些逻辑,并且可以也持有一个模型(他们在接收事件时更新)。
因此,您更有可能使用 dataLoaderModule 而不是 dataModule 来发布数据模型(例如 {data: 3} ), 一旦他完成加载。
您设置的另一个重要要求是在避免全局实例变量的同时共享数据 - 这是一个非常重要的概念,也是朝着正确方向迈出的一步。您在解决方案中错过的是 - 依赖注入(inject)或至少一个允许定义依赖项的模块系统。
您知道,拥有一个事件驱动的应用程序并不一定意味着每一段代码 都应该使用事件进行通信。应用程序配置模型或实用程序服务绝对是我要注入(inject)的东西(当使用 DI 时,比如在 Angular 中)、要求(当使用 AMD/CommonJS 时)或导入(当使用 ES6 模块时)。
(即,而不是使用事件与实用程序通信)。
在您的示例中,不清楚 configModule 是静态应用程序配置还是我可以从 UI 调整的某个旋钮。如果它是静态应用程序配置 - 我会注入(inject)它。
现在,让我们看一些例子:
假设如下:
dataLoaderModule 而不是 dataModuleconfigModule 是静态配置模型。我们会:
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),而是需要(或注入(inject))我们的依赖项。
在这里您可能会问:“如果我的意思是从 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});
});
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(又名显示模块)
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();
});
});
就是这样。
希望对您有所帮助:)
如果没有 - 评论!
关于javascript - 在不同模块之间调解和共享数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36189605/
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c