AngularJS之代码风格36条建议【一】(九)
前言
其实在新学一门知识时,我们应该注意下怎么书写代码更加规范,从开始就注意养成一个良好的习惯无论是对于bug的查找还是走人后别人熟悉代码都是非常好的,利人利己的事情何乐而不为呢,关于AngularJS中的代码风格分为几节来阐述。希望对打算学习AngularJS的新手或者已经在路上的老手有那么一丢丢的帮助也是可以的。
普遍规则
tips 01(定义一个组件脚本文件时,建议此文件的代码少于400行)
(1)有利于单元测试和模拟测试。
(2)增加可读性、可维护性、避免和团队在源代码控制上的冲突。
(3)当在文件中组合组件时,可能会共享变量、依赖不需要的耦合从而避免潜在的bugs。
避免如下这样做:
angular.module('app', ['ngRoute']).controller('SomeController', SomeController).factory('someFactory', someFactory);function SomeController() { }function someFactory() { }
相同的组件应该分为各自的文件(推荐如下做):
// app.module.js angular.module('app', ['ngRoute']);
angular.module('app').controller('SomeController', SomeController);function SomeController() { }
angular.module('app').factory('someFactory', someFactory);function someFactory() { }
JavaScript作用域
tips 02 (包含Angular的组件应该作为匿名函数立即被调用)
(1)匿名函数移除了全局作用域中的变量,能够避免变量冲突以及变量长期存在于内存中。
(2)当代码经过捆绑和压缩到单个文件中,并将其文件部署到生产服务器中时会产生全局变量的冲突。
避免如下这样做:
angular.module('app').factory('logger', logger);function logger() { }angular.module('app').factory('storage', storage);function storage() { }
推荐如下做:
(function() {'use strict';angular.module('app').factory('logger', logger);function logger() { } })();
(function() {'use strict';angular.module('app').factory('storage', storage);function storage() { } })();
定义模块
tips 03 (声明模块时不要用变量来返回)
一个文件中的组件,很少使用需要引入一个变量的模块。
避免如下这样做:
var app = angular.module('app', ['ngAnimate','ngRoute','app.shared','app.dashboard' ]);
推荐如下做:
angular.module('app', ['ngAnimate','ngRoute','app.shared','app.dashboard']);
使用模块
tips 04(使用模块时避免使用变量代替的应该是链式语法)
将使代码更加可读,避免变量的冲突和泄漏。
避免如下这样做:
var app = angular.module('app'); app.controller('SomeController', SomeController);function SomeController() { }
推荐如下这样做:
angular.module('app').controller('SomeController', SomeController);function SomeController() { }
命名函数vs匿名函数
tips 05 (使用命名函数来作为函数的回调而非匿名函数)
使代码易读,易于调试且降低嵌套代码的回调量。
避免如下这样做:
angular.module('app').controller('DashboardController', function() { }).factory('logger', function() { });
推荐如下这样做:
angular.module('app').controller('DashboardController', DashboardController);function DashboardController() { }
angular.module('app').factory('logger', logger);function logger() { }
控制器
tips 06(使用controllerAs语法代替$scope语法)
避免如下:
<div ng-controller="CustomerController">{{ name }} </div>
推荐如下:
<div ng-controller="CustomerController as customer">{{ customer.name }} </div>
tips 07(使用控制器内部使用controllerAs语法代替scope语法即再内部用this代替scope语法即再内部用this代替scope)
避免如下:
function CustomerController($scope) {$scope.name = {};$scope.sendMessage = function() { }; }
推荐如下:
function CustomerController() {this.name = {};this.sendMessage = function() { }; }
tips 08(使用VM代替controllerAs语法即使用一个变量来捕获this,如VM,它代表ViewModel。)
this关键字代表上下文,在控制器内部使用函数时可能会改变它的上下文,用一个变量来捕获this能够避免面临这样的问题。
避免如下:
function CustomerController() {this.name = {};this.sendMessage = function() { }; }
推荐如下:
function CustomerController() {var vm = this;vm.name = {};vm.sendMessage = function() { }; }
tips 09(在控制器的最顶部按照字母大小来排序,而非通过控制器代码来进行扩展)
(1)在顶部绑定成员易于阅读同时帮助我们识别可以在控制器中绑定的成员并在视图中使用。
(2)使用匿名函数虽然可能,但是一旦代码量超过一定数量则降低了代码的可阅读性。
避免如下:
function SessionsController() {var vm = this;vm.gotoSession = function() {/* ... */};vm.refresh = function() {/* ... */};vm.search = function() {/* ... */};vm.sessions = [];vm.title = 'Sessions'; }
推荐如下:
function SessionsController() {var vm = this;vm.gotoSession = gotoSession;vm.refresh = refresh;vm.search = search;vm.sessions = [];vm.title = 'Sessions';////function gotoSession() {/* */}function refresh() {/* */}function search() {/* */} }
tips 10 (使用声明式函数来隐藏实现细节)
使用声明式函数来隐藏实现细节,并保持绑定的成员在顶部。当在控制器中需要绑定一个函数时,指向它到一个函数式声明紧接着在下面。即将成员绑定在顶部且使用声明式函数。
避免如下:
function AvengersController(avengersService, logger) {var vm = this;vm.avengers = [];vm.title = 'Avengers';var activate = function() {return getAvengers().then(function() {logger.info('Activated Avengers View');});}var getAvengers = function() {return avengersService.getAvengers().then(function(data) {vm.avengers = data;return vm.avengers;});}vm.getAvengers = getAvengers;activate(); }
推荐如下:
function AvengersController(avengersService, 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 avengersService.getAvengers().then(function(data) {vm.avengers = data;return vm.avengers;});} }
tips 11(在控制器中通过服务和工厂将业务逻辑导入其中)
(1)业务逻辑可能在多个控制器中被重用,将服务通过函数进行暴露。
(2)在单元测试中,业务逻辑更容易被隔离,在控制器中进行调用时更容易被模拟。
(3)消除了依赖且在控制器中隐藏了实现的细节。
避免如下:
function OrderController($http, $q, config, userInfo) {var vm = this;vm.checkCredit = checkCredit;vm.isCreditOk;vm.total = 0;function checkCredit() {var settings = {};return $http.get(settings).then(function(data) {vm.isCreditOk = vm.total <= maxRemainingAmount}).catch(function(error) {});}; }
推荐如下:
function OrderController(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(showError);}; }
tips 12 (保持控制器关注)
对一个视图定义一个控制器,对于其他控制器不要重用控制器,代替的是将重用逻辑移到工厂以此来保持控制器简单,更多的是关注视图。
tips 13(分配控制器)
当控制器必须和一个视图配对并且组件会被其他控制器和视图重用时,通过路由来定义控制器。
避免如下:
angular.module('app').config(config);function config($routeProvider) {$routeProvider.when('/avengers', {templateUrl: 'avengers.html'}); }<div ng-controller="AvengersController as vm"> </div>
推荐如下:
angular.module('app').config(config);function config($routeProvider) {$routeProvider.when('/avengers', {templateUrl: 'avengers.html',controller: 'Avengers',controllerAs: 'vm'}); }<div> </div>
服务
tips 14(单例)
服务被初始化通过new关键字,使用this关键字来修饰方法和变量,因为所有的服务是单例对象,所以对于每个injector的服务只有唯一的实例。
推荐如下:
// 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) {/* */}}; }
工厂
tips 15(将访问成员置顶)
(1)在顶部暴露要调用的服务的成员,加强可读性以及单元测试。
(2)当文件足够大时,可能需要滚动才能看到其暴露的函数。
(3)通过服务定义的接口在代码量超过100行时避免降低代码的可阅读性和造成更多的滚动。
避免如下:
function dataService() {var someValue = '';function save() {/* */};function validate() {/* */};return {save: save,someValue: someValue,validate: validate}; }
推荐如下:
function dataService() {var someValue = '';var service = {save: save,someValue: someValue,validate: validate};return service;////function save() {/* */};function validate() {/* */}; }
服务
tips 16 (重构服务)
对于数据操作和将数据与工厂进行交互时重构逻辑,使数据服务负责ajax等或其他操作。
推荐如下:
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);}} }
指令
tips 17(为每个指令定义一个文件,并以此指令命名)
(1)很容易将所有指令混合在一个文件中,但是很难对于共享跨应用程序或者共享模块等。
(2)每一个文件一个指令利于代码的可维护性。
避免如下:
angular.module('app.widgets').directive('orderCalendarRange', orderCalendarRange).directive('salesCustomerInfo', salesCustomerInfo)function orderCalendarRange() {}function salesCustomerInfo() { }
推荐如下:
angular.module('sales.order').directive('acmeOrderCalendarRange', orderCalendarRange);function orderCalendarRange() { }
angular.module('sales.widgets').directive('acmeSalesCustomerInfo', salesCustomerInfo);function salesCustomerInfo() { }
tips 18 (提供唯一的指令前缀)
提供一个短的、唯一的、描述性的指令前缀。例如cnblogsIngUserInfo,则在html中被声明为cnblogs-ing-user-info。
可以用唯一的指令前缀来标识指令的背景和来源,例如上述的cnblogsIngUserInfo,cnblogs代表博客园,而Ing代表闪存,User代表用户,info代表信息。
tips 19(对元素和特性进行约束)
在AngularJS 1.3+默认的是EA,在此之下需要用Restrict来进行限制。
避免如下:
<div class="my-calendar-range"></div>
推荐如下:
<my-calendar-range></my-calendar-range> <div my-calendar-range></div>
tips 20 (在指令中使用controllerAs语法与控制器和视图中使用该语法要一致)
推荐如下:
<div my-example max="77"></div>
angular.module('app').directive('myExample', myExample);function myExample() {var directive = {restrict: 'EA',templateUrl: 'app/feature/example.directive.html',scope: {max: '='},controller: ExampleController,controllerAs: 'vm'};return directive; }function ExampleController() {var vm = this;vm.min = 3;console.log('CTRL: vm.min = %s', vm.min);console.log('CTRL: vm.max = %s', vm.max); }
<!-- example.directive.html --> <div>hello world</div> <div>max={{vm.max}}<input ng-model="vm.max"/></div> <div>min={{vm.min}}<input ng-model="vm.min"/></div>
tips 21(在指令添加属性bindToController = true)
当在指令中使用controllerAs语法时,若我们想绑定外部作用域到指令的控制器的作用域令bindToController = true。
如上述tips 20初始化文本值为vm.max为undifined,若设置bindToController = true,则vm.max = 77;
解析promise
tips 22(控制器激活promise)
在一个activate函数中来启动控制器的逻辑。
(1)在一致的地方放置启动逻辑有利于问题的定位以及测试,同时避免在跨控制器中传播激活逻辑。
(2)控制器激活可以更方便地重用刷新的控制器或者视图逻辑,保持逻辑在一起,使得更快加载视图。
避免如下:
function AvengersController(dataservice) {var vm = this;vm.avengers = [];vm.title = 'Avengers';dataservice.getAvengers().then(function(data) {vm.avengers = data;return vm.avengers;}); }
推荐如下:
function AvengersController(dataservice) {var vm = this;vm.avengers = [];vm.title = 'Avengers';activate();////function activate() {return dataservice.getAvengers().then(function(data) {vm.avengers = data;return vm.avengers;})} }
tips 23(路由解析promise)
在控制器被激活之前,若控制器依赖于promise需要被解析时,在控制器逻辑执行之前通过$routerProvider来解析这些依赖。在控制器激活之前,如果需要依据条件来取消路由,通过路由解析来进行。
(1)在控制器加载之前之前它可能需要获取数据,数据可能来源于自定义的工厂或$http的promise。使用路由解析使得promise在控制器逻辑执行之前被解析,因此它可能根据在promise的数据来采取不同的动作。
(2)在路由和控制器的激活函数中的代码执行之后,视图开始被正确加载,当激活promise解析时,数据绑定开始进行。
避免如下:
angular.module('app').controller('AvengersController', AvengersController);function AvengersController(movieService) {var vm = this;// unresolved vm.movies;// resolved asynchronouslymovieService.getMovies().then(function(response) {vm.movies = response.movies;}); }
推荐如下:
// route-config.js angular.module('app').config(config);function config($routeProvider) {$routeProvider.when('/avengers', {templateUrl: 'avengers.html',controller: 'AvengersController',controllerAs: 'vm',resolve: {moviesPrepService: function(movieService) {return movieService.getMovies();}}}); }// avengers.js angular.module('app').controller('AvengersController', AvengersController);AvengersController.$inject = ['moviesPrepService']; function AvengersController(moviesPrepService) {var vm = this;vm.movies = moviesPrepService.movies; }
或者推荐如下操作(更易于调试和处理依赖注入):
// route-config.js angular.module('app').config(config);function config($routeProvider) {$routeProvider.when('/avengers', {templateUrl: 'avengers.html',controller: 'AvengersController',controllerAs: 'vm',resolve: {moviesPrepService: moviesPrepService}}); }function moviesPrepService(movieService) {return movieService.getMovies(); }// avengers.js angular.module('app').controller('AvengersController', AvengersController);AvengersController.$inject = ['moviesPrepService']; function AvengersController(moviesPrepService) {var vm = this;vm.movies = moviesPrepService.movies; }
tips 24(用promise来处理异常)
一个promise的catch模块必须要返回一个reject的promise来在promise链中维护异常。
在服务或者工厂中一定要处理异常。
(1)如果一个catch模块没有返回一个reject的promise,那么此时这个promise的调用者不知道异常的出现,接着调用者的then然后被执行,用户根本不知道发生了什么。
(2)避免隐藏的错误以及误导用户。
避免如下:
function getCustomer(id) {return $http.get('/api/customer/' + id).then(getCustomerComplete).catch(getCustomerFailed);function getCustomerComplete(data, status, headers, config) {return data.data;}function getCustomerFailed(e) {var newMessage = 'XHR Failed for getCustomer'if (e.data && e.data.description) {newMessage = newMessage + '\n' + e.data.description;}e.data.description = newMessage;logger.error(newMessage);// ***// Notice there is no return of the rejected promise// *** } }
推荐如下:
function getCustomer(id) {return $http.get('/api/customer/' + id).then(getCustomerComplete).catch(getCustomerFailed);function getCustomerComplete(data, status, headers, config) {return data.data;}function getCustomerFailed(e) {var newMessage = 'XHR Failed for getCustomer'if (e.data && e.data.description) {newMessage = newMessage + '\n' + e.data.description;}e.data.description = newMessage;logger.error(newMessage);return $q.reject(e);} }
手动标注依赖注入
tips 25(手动识别依赖)
使用$inject来识别AngularJS组件中的依赖。
避免如下:
angular.module('app').controller('DashboardController',['$location', '$routeParams', 'common', 'dataservice',function Dashboard($location, $routeParams, common, dataservice) {}]);
避免如下:
angular.module('app').controller('DashboardController',['$location', '$routeParams', 'common', 'dataservice', Dashboard]);function Dashboard($location, $routeParams, common, dataservice) { }
推荐如下:
angular.module('app').controller('DashboardController', DashboardController);DashboardController.$inject = ['$location', '$routeParams', 'common', 'dataservice'];function DashboardController($location, $routeParams, common, dataservice) { }
注意:当函数是如下一个返回语句,此时inject可能无法访问(例如在指令中),此时解决这个问题的办法是将inject可能无法访问(例如在指令中),此时解决这个问题的办法是将inject移动到控制器的外面。
tips 26($inject无效的情况)
避免如下:
function outer() {var ddo = {controller: DashboardPanelController,controllerAs: 'vm'};return ddo;DashboardPanelController.$inject = ['logger']; // Unreachablefunction DashboardPanelController(logger) {} }
推荐如下:
function outer() {var ddo = {controller: DashboardPanelController,controllerAs: 'vm'};return ddo; }DashboardPanelController.$inject = ['logger']; function DashboardPanelController(logger) { }
tips 27(手动解析路由依赖)
使用$inject来手动识别Angular组件的路由解析依赖。
推荐如下:
function config($routeProvider) {$routeProvider.when('/avengers', {templateUrl: 'avengers.html',controller: 'AvengersController',controllerAs: 'vm',resolve: {moviesPrepService: moviesPrepService}}); }moviesPrepService.$inject = ['movieService']; function moviesPrepService(movieService) {return movieService.getMovies(); }
异常处理
tips 28(用decorators来配置处理异常)
配置时使用provider服务,当异常出现时在provider服务,当异常出现时在exceptionHandler中使用decorator来处理异常。
提供一致的方式在运行时来处理未捕获的异常。
推荐如下:
angular.module('blocks.exception').config(exceptionConfig);exceptionConfig.$inject = ['$provide'];function exceptionConfig($provide) {$provide.decorator('$exceptionHandler', extendExceptionHandler); }extendExceptionHandler.$inject = ['$delegate', 'toastr'];function extendExceptionHandler($delegate, toastr) {return function(exception, cause) {$delegate(exception, cause);var errorData = {exception: exception,cause: cause};toastr.error(exception.msg, errorData);}; }
tips 29(创建工厂并暴露其接口来捕获异常)
在代码执行过程中可能会抛出异常,我们需要提供统一的方式来捕获异常。
推荐如下:
angular.module('blocks.exception').factory('exception', exception);exception.$inject = ['logger'];function exception(logger) {var service = {catcher: catcher};return service;function catcher(message) {return function(reason) {logger.error(message, reason);};} }
tips 30(使用document和document和window代替document和window)
在AngularJS中存在document和document和window两个服务来代替document和window利于模拟和测试。
tips 31(使用interval和interval和timeout代替interval和timeout)
在AngularJS中存在interval和interval和timeout两个服务来代替interval和timeout利于测试和处理Angular的digest生命周期从而保持数据同步绑定。
命名
通过使用统一的命名方式来为所有组件命名,推荐的方式为feature.type.js。如下:
文件名:cnblogs.controller.js。
注册的组件名:CnblogsController。
tips 32(文件命名的特点)
避免如下:
// Controllers avengers.js avengers.controller.js avengersController.js// Services/Factories logger.js logger.service.js loggerService.js
推荐如下:
// controllers avengers.controller.js avengers.controller.spec.js// services/factories logger.service.js logger.service.spec.js// constants constants.js// module definition avengers.module.js// routes avengers.routes.js avengers.routes.spec.js// configuration avengers.config.js// directives avenger-profile.directive.js avenger-profile.directive.spec.js
tips 33(控制器命名后缀为Controller)
控制器命名后缀是最常用且更明确、具体的描述。
推荐如下:
angular.module.controller('AvengersController', AvengersController);function AvengersController() { }
tips 34(工厂和服务命名)
根据其特征来统一为所有服务和工厂来命名,使用骆驼风格来命名。避免工厂和服务前缀使用$。
(1)提供一致的方式来快速识别和引用工厂。
(2)避免命名冲突。
(3)清除服务名称,如logger,不需要其后缀。
推荐如下:
// logger.service.js angular.module.factory('logger', logger);function logger() { }
// credit.service.js angular.module.factory('creditService', creditService);function creditService() { }// customer.service.js angular.module.service('customerService', customerService);function customerService() { }
tips 36(指令组件命名)
通过使用骆驼风格来为指令组件统一命名,使用短前缀来描述这个区域信息(例如:前缀可为公司名称或者项目名称)。
提供统一的方式来快速识别和引用组件
推荐如下:
// cnblogs-profile.directive.js angular.module.directive('xxCnblogsProfile', xxCnblogsrProfile);// usage is <xx-cnblogs-profile> </xx-cnblogs-profile>function xxCnblogsProfile() { }
总结
本节我们讲了在AngularJS中的代码风格,我们可以一定不需要这样做,但是我们推荐这样做。
本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/5556836.html,如需转载请自行联系原作者
AngularJS之代码风格36条建议【一】(九)相关推荐
- 如何写出清晰又优雅的Python代码?我们给你这26条建议
来源:大数据DT本文约1900字,建议阅读5分钟PEP 8非常详细地描述了如何编写清晰的Python代码. [ 导读 ] Python Enhancement Proposal #8叫作PEP 8,它 ...
- 列举出php代码级的优化技巧,php 代码优化的42条建议 推荐
php 代码优化的42条建议 推荐 更新时间:2009年09月25日 15:12:25 作者: php开发过程中,经常需要考虑一些性能问题,下面是一些总结. 1.如果一个方法可静态化,就对它做静态 ...
- 满屋花网页代码_成为更好的程序猿!2020年给网页开发人员的32条建议
全文共3315字,预计学习时长10分钟 来源:Pexels 2019年已经过去了,对开发人员来说是充满挑战和机遇的一年.开发人员们学习和掌握了很多--像React.Vue.Angular和Svelte ...
- python代码风格程序越复杂越高级_Python中代码风格的改变和相应的性能优化
使用现代风格改善你的代码 一旦你开始使用 Python 3,你就有机会接触新的特性来改善你的代码.这篇文章中提到的很多东西实际上在 Python 3 之前就已经被支持了.但我还是要提一下它们,因为知道 ...
- 【Java】恭喜你,你的代码获奖了(编写代码的151的建议)
汝之观览,吾之幸也!本文主要摘抄Java代码的151的建议,让你的代码变得更加的优雅,从新手养成编写好代码的习惯. 编写程序是一件优雅的事,所以要学习一些基本的代码规范.本文参考<编写高质量代码 ...
- 汇编程序员之代码风格指南
Style Guidelines for Assembly Language Programmers 汇编程序员之代码风格指南 作者:Randall Hyde http://webster.cs. ...
- 零基础自学Python:安装Python、解释器、代码风格等
Python是一种面向对象.解释型计算机程序设计语言.语法简洁清晰,强制用空白符作为语句缩进.Python具有丰富和强大的库,又被称为胶水语言.能把其他语言(主要C/C++)写的模块很轻松的结合在一起 ...
- 写出漂亮 Python 代码的 20条准则
点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来自 | 架构头条 按照<代码整洁之道>的说法,"花在阅读和编码 ...
- 良好的代码风格养成记
综述 优秀的代码风格如同一身得体的打扮,能够给人以良好的印象.初学程序设计,首先必须建立良好的编程习惯,这其中就包括代码风格.本文就代码风格中的几个重点问题进行了讨论,并在文后给出了一份优秀的代码作为 ...
最新文章
- 【点云论文速读】6D位姿估计
- 三分钟基础:什么是拥塞控制?
- 如果某个字段值相同则触发器新增_Thrift IDL新增字段导致版本不一致引发的惨案...
- c语言中逗号自加,计算机二级自加、自减运算符和逗号运算符
- Druid 配置_DruidDataSource参考配置
- 优酷开放SDK之setOnCompletionListener
- [蓝桥杯][算法训练VIP]黑白无常(dfs)
- 2018你那计算机考试新题型,2018考研新题型考察点和解题步骤
- prim算法_数据结构 7.4.1 最小生成树 Prim
- 语文学科html代码,2016年最新学科分类与代码..doc
- SpringBoot注解把配置文件自动映射到属性和实体类实战
- 中国钢铁产业产量分析与市场需求状况研究报告2022版
- 知道生产日期和保质期来计算有效期至的方法和代码
- 单核性能强的服务器cpu,Cpu单核性能强和多核性能强都有什么用?
- 苹果笔记本恢复服务器上的安装信息已被破坏,Mac电脑如何恢复删除或已损坏无法打开的Word文件?...
- python中查找文件当前位置的命令为tell()_Python文件处理之seek(), tell()用法...
- 基于形态学的图像后期抗锯齿算法--MLAA优化研究
- c语言判断正整数位数 请用strengh,C语言程序设计-4、12章习题解答.doc
- 【Flink】迟到数据的处理
- 电大考试计算机应用基础考试试题,最新电大统考计算机应用基础试题知识点