AngularJS 中的模块和命名空间/名称冲突

Posted

技术标签:

【中文标题】AngularJS 中的模块和命名空间/名称冲突【英文标题】:Modules and namespace / name collision in AngularJS 【发布时间】:2012-11-04 14:24:02 【问题描述】:

考虑下面的 jfiddle http://jsfiddle.net/bchapman26/9uUBU/29/

//angular.js example for factory vs service
var app = angular.module('myApp', ['module1', 'module2']);

var service1module = angular.module('module1', []);

service1module.factory('myService', function() 
    return 
        sayHello: function(text) 
            return "Service1 says \"Hello " + text + "\"";
        ,
        sayGoodbye: function(text) 
            return "Service1 says \"Goodbye " + text + "\"";
        
    ;
);

var service2module = angular.module('module2', []);

service2module.factory('myService', function() 
    return 
        sayHello: function(text) 
            return "Service2 says \"Hello " + text + "\"";
        ,
        sayGoodbye: function(text) 
            return "Service2 says \"Goodbye " + text + "\"";
        
    ;
);

function HelloCtrl($scope, myService) 
    $scope.fromService1 = myService.sayHello("World");


function GoodbyeCtrl($scope, myService) 
    $scope.fromService2 = myService.sayGoodbye("World");
​

我有 2 个模块(模块 1 和模块 2)。 module1 和 module2 都定义了一个名为 myService 的服务。当两个模块都导入 myApp 时,这似乎会在 Angular 中的 myService 上产生名称冲突。看来 AngularJs 只是使用了第二个服务定义,而没有警告您可能存在的问题。

非常大的项目(或通常只是重用模块)会有名称冲突的风险,这可能难以调试。

有没有办法给模块名称加上前缀,这样就不会发生名称冲突?

【问题讨论】:

更新:我发现了一种可能的解决方法,方法是对传递到 di 系统的服务名称应用命名约定。见jsfiddle.net/bchapman26/9uUBU/32 但是,我的主要问题仍然存在:有没有一种本地方法可以避免上面显示的名称冲突? 现在这似乎是个问题,尤其是当您考虑使用 3rd 方组件的潜在用途时。我有一个问题提出here。 我认为这不是问题(实际上是一个功能和设计),因为它对于测试和覆盖行为很有用。命名空间或使用命名约定是要走的路。 【参考方案1】:

截至今天,AngularJS 模块不提供任何类型的命名空间来防止不同模块中的对象之间发生冲突。原因是 AngularJS 应用程序只有一个注入器,它保存所有对象的名称,而不考虑模块名称。

AngularJS Developer Guide 说:

为了管理依赖创建的责任,每个 Angular 应用程序有一个注射器。注入器是一个服务定位器,它是 负责构建和查找依赖关系。

正如您所提到的,将模块注入您的主/应用程序模块时可能会导致严重的错误。当碰撞发生时,它们是沉默的,获胜者由最后注入的模块决定。

所以不,没有避免这些冲突的内置方法。也许这将在未来发生。对于更容易出现此问题的大型应用程序,您说得对,命名约定是您的最佳工具。考虑属于模块或功能区域的对象是否可以使用短前缀。

【讨论】:

吉姆,这很好地总结了我的问题。感谢您的回复。 正如在其他帖子中提到的,“hacky”解决方案存在于野外,方法是在服务名称前加上发起它的模块,例如module2.MyService。毕竟,服务名称只是字符串键。依赖模块——来自上例的myApp——将定义MyService在实践中的工作方式。次优但可用。【参考方案2】:

您可以通过使用约定命名模块来避免这种情况,以便它们始终是唯一的。

一种方法是看看其他语言是如何做到的。例如,在 Java 中,类的“全名”基于文件名和它所在的文件夹。例如,如果在 MyArtStuff 文件夹中有一个名为 Bitmap.java 的 Java 文件,则类的全名将成为 MyArtStuff.Bitmap

原来 AngularJS 允许您将点 (.) 作为模块名称的一部分,因此您基本上可以使用名称约定。

例如,如果开发人员在脚本“MainPage\Module1.js”中创建了一个名为“ModuleA”的模块,他们应该将其模块命名为“MainPage.Module1.ModuleA”。因为每个路径和文件名在您的应用程序中都是唯一的,所以您的模块名称将是唯一的。

您只需要让您的开发人员遵守此约定即可。

请注意,正如 Rockallite 指出的那样,这对于在多个模块中具有相同名称的服务、控制器等没有帮助。但是您可以使用类似的方法来得出结果,并为这些元素的名称加上前缀。

理想情况下 AngularJS 应该有命名空间,并且在未来它可能会。在那之前,我们能做的最好的事情就是做开发人员在命名空间被发明之前 40 多年一直在做的事情,并尽我们所能为我们的元素添加前缀。

【讨论】:

这不是关于模块名称,而是关于服务/过滤器/控制器等 - 在这些名称中我真的不会使用点表示法(尤其是不使用 ng-annotate)...... 这与模块名称冲突无关。这是关于不同模块(尤其是来自第 3 方)中的控制器/过滤器/服务名称冲突。由于您直接使用名称引用控制器/过滤器/服务且没有任何命名空间,因此无论您对模块名称做什么都没有帮助。 @Rockallite 防止冲突只是意味着名称必须是唯一的。我不是一个新问题。它存在于所有语言中,较新的语言使用带有点符号的命名空间。 Java 鼓励您使用反向域名来使您的命名空间独一无二。像 jQuery 和 Underscore 这样的库尝试使用晦涩的字符。像 C 这样没有命名空间的语言使用函数名前缀。例如,ObjectiveC 核心库函数使用“NS”前缀来区分自己。我相信 OpenGL 库使用“gl”前缀。您只需要应用相同的原则。 @Rockallite “这是 AngularJS 特有的问题”? “问题”存在于 C 和 ObjectiveC 中,它们也没有命名空间。我在之前的回复中提到的所有内容。而且解决方案在那里“容易出错”和“难以扩展”。 43 年后的今天,C 仍在使用。他们成功了。命名空间是理想的,但几十年来证明这个问题是可控的。 ...手动命名空间很繁琐,容易出错,并且降低了可移植性。参考:github.com/angular/angular.js/issues/2767【参考方案3】:

不幸的是,AngularJS 中没有namespacing。一种解决方案是使用prefix(另一种解决方案可能是this!)。请参阅以下示例:

// root app
const rootApp = angular.module('root-app', ['app1', 'app2']);

// app 1
const app1 = angular.module('app1', []);
app1.controller('app1.main', function($scope) 
  $scope.msg = 'App1';
);

// app2
const app2 = angular.module('app2', []);
app1.controller('app2.main', function($scope) 
  $scope.msg = 'App2';
)
<!-- angularjs@1.7.0 -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.0/angular.min.js"></script>

<!-- root app -->
<div ng-app="root-app">

  <!-- app 1 -->
  <div ng-controller="app1.main">
    msg
  </div>

  <!-- app 2 -->
  <div ng-controller="app2.main">
    msg
  </div>

</div>

【讨论】:

【参考方案4】:

在您希望提供服务的模块上定义您的控制器。

service2Module.controller("ServiceTwoCtrl", function(myService, $scope) );

【讨论】:

使用此建议后,似乎无法解决服务上的名称冲突。 嗯.. 为什么需要两个同名的模块? @AndyJoslin - 反过来,你如何检测到你有名字冲突? @AndrewJoslin 考虑一个您无法控制所有模块的开发位置的应用程序。一些插件类型的架构,多个团队构建组件。 @AndrewJoslin 如果您的应用程序依赖于 3rd-party 模块,您如何避免您自己的模块与其模块之间(以及所有这些 3rd-party 模块之间)的控制器/过滤器/服务名称冲突?

以上是关于AngularJS 中的模块和命名空间/名称冲突的主要内容,如果未能解决你的问题,请参考以下文章

命名空间和类名冲突

C++ 中命名空间的 5 个常见用法

AngularJS 定义中的命名冲突

Python中的命名空间是啥?

处理 R 中冲突的命名空间(不同包中的相同函数名):重置包命名空间的优先级

fortran 模块的子例程名称冲突