AngularJS编程风格指南(中文版)angular-styleguide
http://www.tuicool.com/articles/JbuErq
https://github.com/johnpapa/angular-styleguide/blob/master/a1/i18n/zh-CN.md
The original English version is the source of truth, as it is maintained and updated first.
如果你正在寻找一些关于语法、约定和结构化的Angular应用的一个有建设性的规范,那么你来对地方了。这里所包含的内容是基于我在团队中使用 Angular 的一些经验、一些演讲和 Pluralsight培训课程 。
这个规范的目的是为构建Angular应用提供指导,当然更加重要的是让大家知道我为什么要选择它们。
如果你喜欢这个规范,请在Pluralsight看看 Angular Patterns: Clean Code 。
Community Awesomeness and Credit
Angular社区是一个热衷于分享经验的令人难以置信的社区,尽管Todd Motto(他是我的一个朋友,也是Angular专家)和我合作了多种规范和惯例,但是我们也存在着一些分歧。我鼓励你去看看Todd的指南,在那里你能看到我们之间的区别。
我的许多规范都是从大量的程序会话 Ward Bell 和我所拥有的而来的,我的好友Ward也影响了本规范的最终演变。
在示例App中了解这些规范
看示例代码有助于你更好地理解,你可以在 modular
文件夹下找到 命名为modular的示例应用程序 ,随便克隆。
Angular规范翻译版本。
- 单一职责
- IIFE
- Modules
- Controllers
- Services
- Factories
- Data Services
- Directives
- 解决Controller的Promises
- 手动依赖注入
- 压缩和注释
- 异常处理
- 命名
- 应用程序结构LIFT原则
- 应用程序结构
- 模块化
- 启动逻辑
- Angular $包装服务
- 测试
- 动画
- 注释
- JSHint
- JSCS
- 常量
- 文件模板和片段
- Yeoman Generator
- 路由
- 任务自动化
- Filters
- Angular文档
- 贡献
- 许可
单一职责
[StyleY001]
-
一个文件只定义一个组件。
下面的例子在同一个文件中定义了一个
app
的module和它的一些依赖、一个controller和一个factory。/* avoid */ angular .module('app', ['ngRoute']) .controller('SomeController', SomeController) .factory('someFactory', someFactory); function SomeController() { } function someFactory() { }
推荐以下面的方式来做,把上面相同的组件分割成单独的文件。
/* recommended */ // app.module.js angular .module('app', ['ngRoute']);
/* recommended */ // someController.js angular .module('app') .controller('SomeController', SomeController); function SomeController() { }
/* recommended */ // someFactory.js angular .module('app') .factory('someFactory', someFactory); function someFactory() { }
返回顶部
JavaScript闭包
[StyleY010]
-
把Angular组件包装到一个立即调用函数表达式中(IIFE)。
为什么? :把变量从全局作用域中删除了,这有助于防止变量和函数声明比预期在全局作用域中有更长的生命周期,也有助于避免变量冲突。
为什么? :当你的代码为了发布而压缩了并且被合并到同一个文件中时,可能会有很多变量发生冲突,使用了IIFE(给每个文件提供了一个独立的作用域),你就不用担心这个了。
/* avoid */ // logger.js angular .module('app') .factory('logger', logger); // logger function会被当作一个全局变量 function logger() { } // storage.js angular .module('app') .factory('storage', storage); // storage function会被当作一个全局变量 function storage() { }
/** * recommended * * 再也不存在全局变量了 */ // logger.js (function() { 'use strict'; angular .module('app') .factory('logger', logger); function logger() { } })(); // storage.js (function() { 'use strict'; angular .module('app') .factory('storage', storage); function storage() { } })();
-
注:为了简洁起见,本规范余下的示例中将会省略IIFE语法。
-
注:IIFE阻止了测试代码访问私有成员(正则表达式、helper函数等),这对于自身测试是非常友好的。然而你可以把这些私有成员暴露到可访问成员中进行测试,例如把私有成员(正则表达式、helper函数等)放到factory或是constant中。
返回顶部
Modules
避免命名冲突
[StyleY020]
-
每一个独立子模块使用唯一的命名约定。
为什么 :避免冲突,每个模块也可以方便定义子模块。
定义(aka Setters)
[StyleY021]
-
不使用任何一个使用了setter语法的变量来定义modules。
为什么? :在一个文件只有一个组件的条件下,完全不需要为一个模块引入一个变量。
/* avoid */ var app = angular.module('app', [ 'ngAnimate', 'ngRoute', 'app.shared', 'app.dashboard' ]);
你只需要用简单的setter语法来代替。
/* recommended */ angular .module('app', [ 'ngAnimate', 'ngRoute', 'app.shared', 'app.dashboard' ]);
Getters
[StyleY022]
-
使用module的时候,避免直接用一个变量,而是使用getter的链式语法。
为什么? :这将产生更加易读的代码,并且可以避免变量冲突和泄漏。
/* avoid */ var app = angular.module('app'); app.controller('SomeController', SomeController); function SomeController() { }
/* recommended */ angular .module('app') .controller('SomeController', SomeController); function SomeController() { }
Setting vs Getting
[StyleY023]
-
只能设置一次。
为什么? :一个module只能被创建一次,创建之后才能被检索到。
- 设置module,`angular.module('app', []);`。
- 获取module,`angular.module('app');`。
命名函数 vs 匿名函数
[StyleY024]
-
回调函数使用命名函数,不要用匿名函数。
为什么? :易读,方便调试,减少嵌套回调函数的数量。
/* avoid */ angular .module('app') .controller('Dashboard', function() { }) .factory('logger', function() { });
/* recommended */ // dashboard.js angular .module('app') .controller('Dashboard', Dashboard); function Dashboard () { }
// logger.js angular .module('app') .factory('logger', logger); function logger () { }
回到顶部
Controllers
controllerAs在View中的语法
[StyleY030]
-
使用
controllerAs
语法代替直接用经典的$scope定义的controller的方式。为什么? :controller被构建的时候,就会有一个新的实例,
controllerAs
的语法比经典的$scope语法
更接近JavaScript构造函数。为什么? :这促进在View中对绑定到“有修饰”的对象的使用(例如用
customer.name
代替name
),这将更有语境、更容易阅读,也避免了任何没有“修饰”而产生的引用问题。为什么? :有助于避免在有嵌套的controllers的Views中调用
$parent
。<!-- avoid --> <div ng-controller="Customer"> {{ name }} </div>
<!-- recommended --> <div ng-controller="Customer as customer"> {{ customer.name }} </div>
controllerAs在controller中的语法
[StyleY031]
-
使用
controllerAs
语法代替经典的$scope语法
语法。 -
使用
controllerAs
时,controller中的$scope
被绑定到了this
上。为什么? :
controllerAs
是$scope
的语法修饰,你仍然可以绑定到View上并且访问$scope
的方法。为什么? :避免在controller中使用
$scope
,最好不用它们或是把它们移到一个factory中。factory中可以考虑使用$scope
,controller中只在需要时候才使用$scope
,例如当使用$emit
,$broadcast
,或者$on
来发布和订阅事件时,可以考虑把这些调用挪到factory当中,并从controller中调用。/* avoid */ function Customer ($scope) { $scope.name = {}; $scope.sendMessage = function() { }; }
/* recommended - but see next section */ function Customer () { this.name = {}; this.sendMessage = function() { }; }
controllerAs with vm
[StyleY032]
-
使用
controllerAs
语法时把this
赋值给一个可捕获的变量,选择一个有代表性的名称,例如vm
代表ViewModel。为什么? :
this
在不同的地方有不同的语义(就是作用域不同),在controller中的一个函数内部使用this
时可能会改变它的上下文。用一个变量来捕获this
的上下文从而可以避免遇到这样的坑。/* avoid */ function Customer() { this.name = {}; this.sendMessage = function() { }; }
/* recommended */ function Customer () { var vm = this; vm.name = {}; vm.sendMessage = function() { }; }
-
注:你可以参照下面的做法来避免 jshint 的警告。但是构造函数(函数名首字母大写)是不需要这个的.
/* jshint validthis: true */ var vm = this;
-
注:在controller中用
controller as
创建了一个watch时,可以用下面的语法监测vm.*
的成员。(创建watch时要谨慎,因为它会增加更多的负载)<input ng-model="vm.title"/>
function SomeController($scope, $log) { var vm = this; vm.title = 'Some Title'; $scope.$watch('vm.title', function(current, original) { $log.info('vm.title was %s', original); $log.info('vm.title is now %s', current); }); }
可绑定成员放到顶部
[StyleY033]
-
把可绑定的成员放到controller的顶部,按字母排序,并且不要通过controller的代码传播。
为什么? :易读,可以让你立即识别controller中的哪些成员可以在View中绑定和使用。
为什么? :虽然设置单行匿名函数很容易,但是当这些函数的代码超过一行时,这将极大降低代码的可读性。在可绑定成员下面定义函数(这些函数被提出来),把具体的实现细节放到下面,可绑定成员放到顶部,这会提高代码的可读性。
/* avoid */ function Sessions() { var vm = this; vm.gotoSession = function() { /* ... */ }; vm.refresh = function() { /* ... */ }; vm.search = function() { /* ... */ }; vm.sessions = []; vm.title = 'Sessions';
/* recommended */ function Sessions() { var vm = this; vm.gotoSession = gotoSession; vm.refresh = refresh; vm.search = search; vm.sessions = []; vm.title = 'Sessions'; //////////// function gotoSession() { /* */ } function refresh() { /* */ } function search() { /* */ }
注:如果一个函数就是一行,那么只要不影响可读性就把它放到顶部。
/* avoid */ function Sessions(data) { var vm = this; vm.gotoSession = gotoSession; vm.refresh = function() { /** * lines * of * code * affects * readability */ }; vm.search = search; vm.sessions = []; vm.title = 'Sessions';
/* recommended */ function Sessions(dataservice) { var vm = this; vm.gotoSession = gotoSession; vm.refresh = dataservice.refresh; // 1 liner is OK vm.search = search; vm.sessions = []; vm.title = 'Se