单一职责
- 一个文件只定义一个组件
IIFE
- 使用立即调用函数表达式
Modules
- 每个独立模块使用唯一的命名
- 在文件只有一个组件的情况下不需要为一个模块引入一个变量
- 使用链式语法
- 只能设置一次
- 设置module,
angular.module('app', []);
。 - 获取module,
angular.module('app');
。
- 设置module,
- 回调函数使用命名函数,不要用匿名函数
1 | angular .module('app') .controller('Dashboard', Dashboard); function Dashboard () { } |
Controllers
- 使用controllerAs语法代替直接用经典的$scope定义的controller的方式
1 | <div ng-controller="Customer as customer"> {{ customer.name }} </div> //javascript function Customer () { this.name = {}; this.sendMessage = function() { }; } |
- 使用controllerAs语法时把this赋值给一个可捕获的变量,选择一个有代表性的名称,如vm代表ViewModel
1 | function Customer () { var vm = this; vm.name = {}; vm.sendMessage = function() { }; } |
- 使用下面的做法避免
jshint
的警告。但是构造函数是不需要这个的
1 | /* jshint validthis: true */ var vm = this; |
- 在controller中使用controllAs创建watch时,可以用下面的语法监测vm.*的成员(创建watch时要谨慎,因为它会增加更多的负载)
1 | <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); }); } |
- 可绑定成员放到controller顶部,按字母排序,并且不要通过controller代码传播
- 如果函数只有一行,可以把它放顶部
1 | function Sessions() { var vm = this; vm.gotoSession = gotoSession; vm.refresh = refresh; vm.sessions = []; vm.title = 'Sessions'; //////////// function gotoSession() { /* */ } function refresh() { /* */ } } |
- 使用函数声明来隐藏实现细节,把绑定成员放到顶部
1 | function Avengers(dataservice, logger) { var vm = this; vm.avengers = []; vm.getAvengers = getAvengers; vm.title = 'Avengers'; activate(); function activate() { return getAvengers().then(function() { logger.info('Activated Avengers View'); }); } function getAvengers() { return dataservice.getAvengers().then(function(data) { vm.avengers = data; return vm.avengers; }); } } |
- 通过委派到server和factory中来延迟controller中的逻辑
1 | function Order (creditService) { var vm = this; vm.checkCredit = checkCredit; vm.isCreditOk; vm.total = 0; function checkCredit () { return creditService.isOrderTotalOk(vm.total) .then(function(isOk) { vm.isCreditOk = isOk; }) .catch(showServiceError); }; } |
- 保持controller的专一性
- 一个view只定义一个controller,尽量不要在其它的view中使用这个controller,把可重用的逻辑放到factory中,保证controller只服务当前视图
- 当一个controller必须匹配一个view时或者任何组件可能被其它controller或是view重用时,连同controller的route一起定义
1 | // avengers.html <div ng-controller="Avengers as vm"> </div> // route-config.js angular .module('app') .config(config); function config ($routeProvider) { $routeProvider .when('/avengers', { templateUrl: 'avengers.html', controller: 'Avengers', controllerAs: 'vm' }); } |
Services
- 用new 实例化services,用this实例化公共方法和变量,由于这和factory是类似的,为了统一,推荐使用factory
- 所有的angular services都是单例,意味着第个injector都只有一个实例化的service
1 | // service angular .module('app') .service('logger', logger); function logger () { this.logError = function(msg) { /* */ }; } // factory angular .module('app') .factory('logger', logger); function logger () { return { logError: function(msg) { /* */ } }; } |
Factories
- factory应该是单一职责,这是由上下文进行封装的。一旦一个factory要处理超过单一的目的地,就应该再创建一个
- factory是一个单例,它返回一个包含service成员的对象
- 使用从显露模块模式派生出来的技术把service(它接口)中可调用的成员暴露到顶部
1 | /* recommended */ function dataService () { var someValue = ''; var service = { save: save, someValue: someValue, validate: validate }; return service; //////////// function save () { /* */ }; function validate () { /* */ }; } |
- 函数声明隐藏实现细节,把绑定成员放到顶部,当你需要在controller中绑定一个函数时,把它指向一个函数声明,这个函数声明在文件的后面会出现
1 | function dataservice($http, $location, $q, exception, logger) { var isPrimed = false; var primePromise; var service = { getAvengersCast: getAvengersCast, getAvengerCount: getAvengerCount, getAvengers: getAvengers, ready: ready }; return service; //////////// function getAvengers() { // implementation details go here } function getAvengerCount() { // implementation details go here } function getAvengersCast() { // implementation details go here } function prime() { // implementation details go here } function ready(nextPromises) { // implementation details go here } } |
Data Services
- 把进行数据操作和数据交互的逻辑放到factory中,数据服务负责XHR请求、本地存储、内存存储和其它任何数据操作
1 | angular .module('app.core') .factory('dataservice', dataservice); dataservice.$inject = ['$http', 'logger']; function dataservice($http, logger) { return { getAvengers: getAvengers }; function getAvengers() { return $http.get('/api/maa') .then(getAvengersComplete) .catch(getAvengersFailed); function getAvengersComplete(response) { return response.data.results; } function getAvengersFailed(error) { logger.error('XHR Failed for getAvengers.' + error.data); } } } |
- 数据服务被调用时(如controller),隐藏调用的直接行为
1 | angular .module('app.avengers') .controller('Avengers', Avengers); Avengers.$inject = ['dataservice', 'logger']; function Avengers(dataservice, logger) { var vm = this; vm.avengers = []; activate(); function activate() { return getAvengers().then(function() { logger.info('Activated Avengers View'); }); } function getAvengers() { return dataservice.getAvengers() .then(function(data) { vm.avengers = data; return vm.avengers; }); } } |
- 像$http一样,数据调用返回一个Promise, 在你调用的函数也返回一个promise
为什么 : 可以把promise连在一起,在数据调用完成并resolve或reject这个promise后采取进一步的行为
1 | activate(); function activate() { //step1 return getAvengers().then(function() { //step4 logger.info('Activated Avengers View'); }); } function getAvengers() { //step2 return dataservice.getAvengers() .then(function(data) { //step3 vm.avengers = data; return vm.avengers; }); } |
Directives
- 一个文件只创建一个directive,并依照directive来命名
1 | // customerInfo.directive.js angular .module('sales.widgets') .directive('acmeSalesCustomerInfo', salesCustomerInfo); function salesCustomerInfo() { }; // spinner.directive.js angular .module('shared.widgets') .directive('acmeSharedSpinner', sharedSpinner); function sharedSpinner() { }; |
- 当需要操作DOM时,使用directive,也可以用css设置样式\animation\services\angular模版、ngShow或者ngHide
- 提供一个短小、唯一、具有描述性的directive前缀,例如acmeSalesCustomerInfo在HTML中声明为acme-sales-customer-info
- 限制元素和属性,一般原则是允许EA,1.3+默认使用EA
1 | angular .module('app.widgets') .directive('myCalendarRange', myCalendarRange); function myCalendarRange () { var directive = { link: link, templateUrl: '/template/is/located/here.html', restrict: 'EA' }; return directive; function link(scope, element, attrs) { /* */ } } |
- directive使用controller as语法,和view使用controller as 保持一致
- 当directive中使用了controller as 语法时,使用bindToController = true把父级作用域绑定到directive的controller作用域
- 在activate函数中解决controller的启动逻辑
1 | function Avengers(dataservice) { var vm = this; vm.avengers = []; vm.title = 'Avengers'; activate(); //////////// function activate() { return dataservice.getAvengers().then(function(data) { vm.avengers = data; return vm.avengers; }); } } |
- 当你决定过渡到视图前取消路由,使用route resolve
1 | // route-config.js angular .module('app') .config(config); function config ($routeProvider) { $routeProvider .when('/avengers', { templateUrl: 'avengers.html', controller: 'Avengers', controllerAs: 'vm', resolve: { moviesPrepService: function(movieService) { return movieService.getMovies(); } } }); } // avengers.js angular .module('app') .controller('Avengers', Avengers); Avengers.$inject = ['moviesPrepService']; function Avengers (moviesPrepService) { var vm = this; vm.movies = moviesPrepService.movies; } |
- 用$inject手动添加组件所需要的依赖
- 避免压缩后找不到变量
- 避免创建内嵌的依赖
1 | angular .module('app') .controller('Dashboard', Dashboard); Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice']; function Dashboard($location, $routeParams, common, dataservice) { } |