AngularJS的模块声明最佳实践?


115

我的应用程序中声明了一堆Angular模块。我最初开始使用“链式”语法声明它们,如下所示:

angular.module('mymodule', [])
    .controller('myctrl', ['dep1', function(dep1){ ... }])
    .service('myservice', ['dep2', function(dep2){ ... }])
    ... // more here

但是我认为这不太容易阅读,所以我开始使用如下模块变量来声明它们:

var mod = angular.module('mymodule', []);

mod.controller('myctrl', ['dep1', function(dep1){ ... }]);

mod.service('myservice', ['dep2', function(dep2){ ... }]);
...

第二种语法对我来说似乎更具可读性,但是我唯一的抱怨是该语法将mod变量保留在全局范围之外。如果我有其他名为的变量mod,那么下一个变量(以及与全局变量相关的其他问题)将覆盖它。

所以我的问题是,这是最好的方法吗?还是做这样的事会更好?

(function(){
    var mod = angular.module('mymod', []);
    mod.controller('myctrl', ['dep1', function(dep1){ ... }]);
    mod.service('myservice', ['dep2', function(dep2){ ... }]);
    ...
})();

还是足够重要呢?只是想知道模块声明的“最佳实践”是什么。提前致谢。


Answers:


118

声明模块的“最佳”方法

由于angular本身在全局范围内,并且模块已保存到其变量中,因此您可以通过angular.module('mymod')以下方式访问模块:

// one file
// NOTE: the immediately invoked function expression 
// is used to exemplify different files and is not required
(function(){
   // declaring the module in one file / anonymous function
   // (only pass a second parameter THIS ONE TIME as a redecleration creates bugs
   // which are very hard to dedect)
   angular.module('mymod', []);
})();


// another file and/or another anonymous function
(function(){   
 // using the function form of use-strict...
 "use strict";
  // accessing the module in another. 
  // this can be done by calling angular.module without the []-brackets
  angular.module('mymod')
    .controller('myctrl', ['dep1', function(dep1){
      //..
    }])

  // appending another service/controller/filter etc to the same module-call inside the same file
    .service('myservice', ['dep2', function(dep2){ 
    //... 
    }]);

  // you can of course use angular.module('mymod') here as well
  angular.module('mymod').controller('anothermyctrl', ['dep1', function(dep1){
      //..
  }])
})();

不需要其他全局变量。

当然,这完全取决于偏好,但是我认为这是最好的做法,因为

  1. 您不必污染全球范围
  2. 您可以随处访问模块并将其及其功能随意分类到不同的文件中
  3. 您可以使用“使用严格”功能形式;
  4. 文件的加载顺序无关紧要

用于对模块和文件进行排序的选项

这种声明和访问模块的方式使您非常灵活。您可以通过函数类型(如另一个答案中所述)或通过路由对模块进行排序,例如:

/******** sorting by route **********/    
angular.module('home')...
angular.module('another-route')...
angular.module('shared')...

最终如何排序取决于个人喜好以及项目的规模和类型。我个人喜欢将模块的所有文件归入同一文件夹(按指令,控制器,服务和过滤器的子文件夹排序)中的所有文件,包括所有不同的测试文件,因为它使您的模块更具可重用性。因此,在中型项目中,我最终得到一个基本模块,该模块包括所有基本路径及其控制器,服务,指令和或多或少复杂的子模块,当我认为它们也可能对其他项目有用时,例如:

/******** modularizing feature-sets **********/
/controllers
/directives
/filters
/services
/my-map-sub-module
/my-map-sub-module/controllers
/my-map-sub-module/services
app.js
...

angular.module('app', [
  'app.directives',
  'app.filters',
  'app.controllers',
  'app.services',
  'myMapSubModule'
]);

angular.module('myMapSubModule',[
   'myMapSubModule.controllers',
   'myMapSubModule.services',
   // only if they are specific to the module
   'myMapSubModule.directives',
   'myMapSubModule.filters'
]);

对于大型项目,有时我会如上所述按路线或按某些选定的主要路线甚至是路线与某些选定组件的组合对模块进行分组,但这确实取决于实际情况。

编辑: 只是因为它是相关的,并且我最近又遇到了这个问题:请注意,您只能创建一次模块(通过向angular.module-function添加第二个参数)。这会使您的应用程序混乱,并且很难检测。

2015年编辑分拣模块: 一个后来的角度体验了半年,我可以添加使用你的应用程序中的不同模块名字的好处是比较有限的AMD仍然没有真正与角和服务,指令和过滤器工作良好无论如何都可以在角度上下文中全局使用(如此处所示)。但是,仍然存在语义和结构上的好处,并且能够包含/排除带有注释掉或注释掉一行代码的模块可能会有所帮助。

由于子模块通常彼此依赖,因此按类型分隔子模块(例如,“ myMapSubModule.controllers”)几乎也没有多大意义


7
你不需要IIFE(立即调用函数表达式),又名匿名自我执行功能
加锁存

1
你是对的。仅在要应用“使用严格”的功能形式时才需要它;虽然没有伤害。
雨果·德·洪里格2013年

1
在大多数情况下,您也可以将其'use strict';放入组件中。module.controller(function () { 'use strict'; ... });
杰克逊

我喜欢实现,但是我也不喜欢链接,所以我将它与Beterraba正在做的事情混合在一起
Mirko

1
使用AMD时,一个名为app的模块就足够了。可以通过删除require语句来排除AMD模块。而且我们不再需要注册控制器和服务。
詹姆斯

28

我喜欢Johnpapa撰写的angular-styleguide,这里有一些与此问题相关的规则:

规则:命名函数与匿名函数

避免使用匿名函数:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', function() { })

而是使用命名函数:

// dashboard.js
angular
  .module('app')
  .controller('Dashboard', Dashboard);

function Dashboard() { }

正如作者所说: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

规则:每个文件定义1个组件。

避免在一个文件中包含多个组件:

angular
  .module('app', ['ngRoute'])
  .controller('SomeController', SomeController)
  .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

Intead,使用一个文件定义模块:

// app.module.js
angular
  .module('app', ['ngRoute']);

一个文件仅使用模块来定义组件

// someController.js
angular
  .module('app')
  .controller('SomeController', SomeController);

function SomeController() { }

和另一个文件来定义另一个组件

// someFactory.js
angular
  .module('app')
  .factory('someFactory', someFactory);

function someFactory() { }

当然,模块,控制器和服务还有许多其他规则,这些规则非常有用并且值得一读。

并感谢ya_dimon的注释,以上代码应包装在IIFE中,例如:

(function (window, angular) {
  angular.module('app')
   .controller('Dashboard', function () { });
})(window, window.angular);

好的答案和良好的链接。
Ellesedil 2015年

如果我在不同的javascript文件中具有不同的控制器,是否需要加载更多数量的文件(即,更多服务器点击次数)?
Vignesh Subramanian

用gulp或grunt,vignesh合并/丑化/重命名它们很容易,而且我个人也喜欢gulp。
aqingsao 2015年

1
您忘了补充,所有这些代码片段都应该放在IIFE中,否则全局上就具有类似“ someFactory()”的功能。有可能发生名称冲突。(并且您在es6中不需要IIFE)
ya_dimon

12

我最近也有这个难题。我就像使用链式语法一样开始,但是从长远来看,对于大型项目而言,它变得笨拙。通常,我会在单独的文件中创建一个控制器模块,一个服务模块等,然后将它们注入到另一个文件中的主应用程序模块中。例如:

// My Controllers File
angular.module('my-controllers',[])
    .controller('oneCtrl',[...])
    .controller('twoCtrl',[...]);

// My Services File
angular.module('my-services',[])
    .factory('oneSrc',[...])
    .facotry('twoSrc',[...]);

// My Directives File
angular.module('my-directives',[])
    .directive('oneDrct',[...])
    .directive('twoDrct',[...]);

// My Main Application File
angular.module('my-app',['my-controllers','my-services','my-directives',...]);

但是随着项目的发展,这些文件中的每一个都变得越来越大。因此,我决定根据每个控制器或服务将它们分解为单独的文件。我发现不使用angular.module('mod-name').注入数组就可以使用它。在一个文件中声明一个全局变量,并期望该变量在另一个文件中随时可用,这是行不通的,否则可能会导致意外的结果。

简而言之,我的应用程序看起来像这样:

// Main Controller File
angular.module('my-controllers',[]);

// Controller One File
angular.module('my-controllers').controller('oneCtrl',[...]);

//Controller Two File
angular.module('my-controllers').controller('twoCtrl',[...]);

我也对服务文件进行了此操作,无需更改主应用程序模块文件,而您仍然可以向其中注入相同的模块。


1
为服务/指令/控制器创建单独的模块有什么意义?
Filip Sobczak 2014年

2
在大型项目中,将控制器/过滤器/指令/服务全部交织在一起时,会很难找到事情。这只是使事情井井有条的一种方法。
meconroy 2014年

1
@FilipSobczak他没有为服务/指令/控制器创建单独的模块。相反,他只使用一次创建了模块angular.module('my-controllers',[]);(请注意,他只指定一次用于声明)。他只是在其他文件中重复使用了此功能。文件分离使维护项目相对容易,尤其是大型项目。
Devner'1

8

另一种实践是将控制器,指令等填充到它们自己的模块中,然后将这些模块注入“主要”模块中:

angular.module('app.controllers', [])
  .controller('controller1', ['$scope', function (scope) {
    scope.name = "USER!";
  }]);

angular.module('app.directives', [])
  .directive('myDirective', [function () {
    return {
      restrict: 'A',
      template: '<div>my directive!</div>'
    }
  }]);

angular.module('app', [
  'app.controllers',
  'app.directives'
]);

全局范围内一无所有。

http://plnkr.co/edit/EtzzPRyxWT1MkhK7KcLo?p=preview


为什么要使用app.controllersinsted of controllers作为模块名称,有什么好处吗?我是Angularjs的新手
sijo vijayan

4

我喜欢划分文件和模块。

像这样:

app.js

var myApp = angular.module('myApp', ['myApp.controllers', 'myApp.directives', 'myApp.services']);

myApp.config(['$routeProvider', function($routeProvider) {
    /* routes configs */
    $routeProvider.when(/*...*/);
}]);

指令.js

var myDirectives = angular.module('myApp.directives', []);

myDirectives.directive( /* ... */ );

service.js

var myServices = angular.module('myApp.services', []);

myServices.factory( /* ... */ );

我不是“链式风格”的忠实拥护者,因此我宁愿总是写下我的变量。


2
这就是我一直这样做的方式,但是,在大规模项目中,每个services.js或controller.js文件都会变得很大,最终您需要将每个服务或控制器分解为一个单独的文件。
meconroy 2013年

1
@meconroy确实如此。当事情变得越来越大时,我喜欢将指令分成较小的模块,然后注入到“ main”指令模块中。
2013年


0

对我来说,链接是最紧凑的方式:

angular.module("mod1",["mod1.submod1"])

 .value("myValues", {
   ...
 })

 .factory("myFactory", function(myValues){
   ...
 })

 .controller("MainCtrl", function($scope){

   // when using "Ctrl as" syntax
   var MC = this;
   MC.data = ...;
 })
 ;

这样,我可以轻松地在模块之间移动组件,无需重复声明同一模块两次,也不需要任何全局变量。

如果文件太长,解决方案很简单-分成两个文件,每个文件在顶部声明其自己的模块。为了提高透明度,我尝试为每个文件保留一个唯一的模块,并将其命名为类似于文件的完整路径。这样,我也无需编写没有的模块[],这是一个常见的痛点。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.