本文是依据 Angular Style Guide 对 Angular 常用模块书写建议的翻译和总结,仅供参考。

IIFE

使用 立即执行函数表达式(Immediately Invoked Function Expression)将 Angular 组件包裹起来,防止污染全局作用域 Style Y010 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* avoid */
// storage.js
angular
.module('app')
.factory('storage', storage); // storage function is added as a global variable
function storage() { } /**
* recommended
*
* no globals are left behind
*/
// storage.js
(function() {
'use strict'; angular
.module('app')
.factory('storage', storage); function storage() { }
})();

Modules

使用 setter 定义 Modules ,避免使用变量 [Style Y021]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* avoid */
var app = angular.module('app', [
'ngAnimate',
'ngRoute',
'app.shared',
'app.dashboard'
]);
/* recommended */
angular
.module('app', [
'ngAnimate',
'ngRoute',
'app.shared',
'app.dashboard'
]);

使用链式的 getter 来获取 Modules ,避免使用变量 [Style Y022] 。尽量不直接使用匿名函数,而是把一个函数名作为回调传进去 [Style Y024] 。

1
2
3
4
5
angular
.module('app')
.controller('SomeController', SomeController); function SomeController() { }

Controller

使用 controllerAs (和 vm 一起)

因为 this 是上下文相关的,为了避免 Controlller 内部的函数在使用 this 时导致上下文改变,应该在一开始使用一个变量(最好统一为 viewModel 的缩写 - vm)来捕获 this [Style Y032] 。

建议书写方式:

1
2
3
4
5
function CustomerController() {
var vm = this;
vm.name = {};
vm.sendMessage = function() {};
}

使用 controllerAs 语法,在把 Controller 和 View 配对[Style Y038] 时,使用这种方式:

1
2
3
4
5
6
7
8
function config($routeProvider) {
$routeProvider
.when('/avengers', {
templateUrl: 'avengers.html',
controller: 'Avengers',
controllerAs: 'vm'
});
}

仅在需要使用 $emit、 $broadcast、 $on、 $watch 等 $scope 下的方法,再使用 $scope[Style Y031] ,书写方式如下:

1
2
3
4
5
6
7
8
9
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 最前面一部分,按字母顺序排列,并且不让代码蔓延。这样能让代码更易读、易查找 [Style Y33] 。

虽然类似于下面这种写法很简便,但是那些超过一行代码的函数会降低可读性。

1
2
3
4
5
6
7
8
9
10
/* avoid */
vm.refresh = function() {
/**
* lines
* of
* code
* affects
* readability
*/
};

建议书写方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* recommended */
function SessionsController() {
var vm = this; vm.gotoSession = gotoSession;
vm.refresh = sessionDataService.refresh; // 1 liner is OK
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions'; //////////// function gotoSession() {
/* */
} function search() {
/* */
}
}

其他

  • 将 Controller 中的部分逻辑放在 Service 或 Factory 中,保持 Controller 的简洁 [Style Y035]
  • 不要对多个 Views 使用同一 Controller,如果有可复用代码,应该放到 Factory 中,保持 Controller 专注于它自己的 View [Style Y037]

Service && Factory

Service

Angular 中的 Service 会通过 new 关键字被实例化,其中的方法和属性会被直接添加在 this 上。因此,通常可以使用 Factory 代替 Service [Style Y040] 。

所有的 Angular Service 都是单例的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 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) {
/* */
}
};
}

Factory

Factory 的创建应该符合单一职责原则 [Style Y050] ,和 Service 一样,Factory 也是单例的,它返回一个包含 Service 中成员的对象,Factory 和 Service 的区别可参见 AngularJS 中 Provider 们 一文。

建议将 Factory 中可访问的成员放在顶部 [Style Y052] :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function logger($log) {
var service = {
error: error,
info: info
}
return service; ////////////
function error() {
/* */
};
function info() {
/* */
};
}

Data Service

将产生数据和与数据交互的操作放在一个 DataService 的 Factory 中,让其负责 XHR 调用、local storage 等任何与数据相关的操作。这样能让 Controller 专注于展示和为 View 层收集信息上 [Style Y060] 。

Controller 不需要关心数据是怎么得到的,而只应该知道从谁那里拿数据。

一种建议的 dataservice 书写方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
}
}
}

当调用一个返回 promise 的 dataservice 时,在调用函数中,也返回一个 promise,方便后续的链式处理 [Style Y061]。 调用 dataservice 的 Controller 的写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
angular
.module('app.avengers')
.controller('AvengersController', AvengersController); AvengersController.$inject = ['dataservice', 'logger']; function AvengersController(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;
});
}
}

Directive

将跟 DOM 相关的操作都放在 Directive 中。在能使用 CSS 设置样式,使用 animation services 设置动画及使用 Angular templating、ngShow 或者 ngHide 的情况下,尽量避免使用 Directive [Style Y072] 。

为每个指令单独创建一个文件,这样能方便跨应用共享,并且便于查找 [Style Y070]。还有,为指令提供一个简短唯一的前缀 [Style Y072] 。

将指令限定为 Elements 和 Attributes,这(EA) 在 Angular 1.3+ 中已经是默认设置 [Style Y074] 。

为保持一贯性,在 Directive 中同样应该使用 controllerAs 语法来将 Controller 和 View 配对 [Style Y075] 。由于 Directive 的 Controller 是在 Directive 闭包外面的,所以,如果想将外层 scope 和 Directive 中 Controller 的 scope 绑定,(Angular 1.3+)可以设置 bindToController = true[Style Y076]。

下面是一个完整的示例。

主文件:

1
<div my-example max="77"></div>

example.directive.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
angular
.module('app')
.directive('myExample', myExample); function myExample() {
var directive = {
restrict: 'EA',
templateUrl: 'app/feature/example.directive.html',
scope: {
max: '='
},
controller: ExampleController,
controllerAs: 'vm',
bindToController: true
}; 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 文件:

1
2
3
<div>hello world</div>
<div>max={{vm.max}}<input ng-model="vm.max"/></div>
<div>min={{vm.min}}<input ng-model="vm.min"/></div>

Dependency Injection

由于AngularJS是通过构造函数的参数名字来推断依赖服务名称的。所以如果要压缩JS代码,它所有的参数也同时会被压缩,这时候依赖注入系统就不能正确的识别出服务了 [Style Y090] 。

有两种方法可以解决这个问题:

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
angular
.module('app')
.controller('DashboardController',
['$routeParams', 'dataservice',
function Dashboard($routeParams, dataservice) {}
]);
``` 方法二 ```javascript
angular
.module('app')
.controller('DashboardController', DashboardController); DashboardController.$inject = ['$routeParams', 'dataservice']; function DashboardController($routeParams, dataservice) {
}

从易于阅读的角度考虑,建议第二种 [Style Y091] 。

当然,如果使用自动化构建工具 Gulp 或 Grunt 的话,还有一种更好的办法,使用 ng-annotate,自动生成 DashboardController.$inject 部分的代码 [Style Y100] ,如下:

1
2
3
4
5
6
7
angular
.module('app')
.controller('DashboardController', DashboardController); /* @ngInject */
function DashboardController($routeParams, dataservice) {
}

AngularJS 常用模块书写建议的更多相关文章

  1. CSS书写建议参考

    总结一些CSS书写建议提供大给家参考,这些是参考了一些文章以及我的个人经验总结出来. 1.能缩写的就尽量缩写吧,毕竟谁都不想多些一些也可以提高阅读体验.包括类名.颜色和css属性.

  2. Ansible Ad-Hoc与常用模块

    ansible 执行结果信息–各颜色说明:ansible Ad-Hoc 说明:ansible 如何查看帮助文档与常用模块详解 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有 ...

  3. AngularJS常用插件与指令收集

    angularjs 组件列表 bindonce UI-Router Angular Tree angular-ngSanitize模块-$sanitize服务详解 使用 AngularJS 开发一个大 ...

  4. python学习笔记之常用模块(第五天)

    参考老师的博客: 金角:http://www.cnblogs.com/alex3714/articles/5161349.html 银角:http://www.cnblogs.com/wupeiqi/ ...

  5. Day05 - Python 常用模块

    1. 模块简介 模块就是一个保存了 Python 代码的文件.模块能定义函数,类和变量.模块里也能包含可执行的代码. 模块也是 Python 对象,具有随机的名字属性用来绑定或引用. 下例是个简单的模 ...

  6. python常用模块(2)

    之前学了两个常用的模块collections和re模块今天我们接着学习其他几个常用模块.都是比较常用的之前的学习或多或少也有所接触比如说时间模块等. 预习: 写一个验证码 首先 要有数字 其次 要有字 ...

  7. AngularJS -- Module (模块)

    点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ 什么是AngularJS的模块 我们所说的模块,是你的AngularJS应用程序的一个组 ...

  8. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

    python全栈开发笔记第二模块 第四章 :常用模块(第二部分)     一.os 模块的 详解 1.os.getcwd()    :得到当前工作目录,即当前python解释器所在目录路径 impor ...

  9. Ansible运维自动化工具19个常用模块使用实例【转】

    一.模块列表 1.setup 2.ping 3.file 4.copy 5.command 6.shell 7.script 8.cron 9.yum 10.service 11.group 12.u ...

随机推荐

  1. linux开放关闭防火墙端口

    原文:http://blog.csdn.net/fengspg/article/details/21337617 1) 重启后生效 开启: chkconfig iptables on 关闭: chkc ...

  2. 搞定android多点触摸模拟

    原理在android 创建多点触摸虚拟设备,然后往设备写模拟数据可以

  3. 谈谈我用Unity5的AssetBundle踩到的几个坑

    在上段时间摸索了Unity5的assetbundle用法之后,我在项目里面全面的使用起来,于是发现了一些坑,这里和大家分享一下,顺便说说我是怎样解决的. 首先是图集打包的问题.这个问题在unity5. ...

  4. VC获得控制台HWND GetConsoleHwnd

    HWND GetConsoleHwnd(void) { #define MY_BUFSIZE 1024 // Buffer size for console window titles. HWND h ...

  5. Python学习 —— 阶段综合练习二

    综合之前的类的学习,做以下实例练习:(建议先不要看代码,自己先试着写:代码仅供参考,有多种实现方法) 1. Triangle  & Equilateral 1). 创建class Triang ...

  6. BZOJ 2179 FFT快速傅立叶 题解

    bzoj 2179 Description 给出两个n位10进制整数x和y,你需要计算x*y. [题目分析] 高精裸题.练手. [代码] 1.手动高精 #include<cstdio> # ...

  7. 数学图形(2.13)Spherical trochoid曲线

    该曲线与上一节的herical cycloid球面外摆曲线 很相似,难道这是球面内摆曲线? #http://www.mathcurve.com/courbes3d/cycloidspheric/tro ...

  8. Hadoo生态中pHive HBase 项目的区别

    http://jenmhdn.iteye.com/blog/1678789 导读:Apache Hive是一个构建于Hadoop(分布式系统基础架构)顶层的数据仓库,Apache HBase是运行于H ...

  9. C#的四个基本技巧

    1.如果可能尽量使用接口来编程 .NET框架包括类和接口,在编写程序的时候,你可能知道正在用.NET的哪个类.然而,在这种情况下如果你用.NET支持的接口而不是它的类来编程时,代码会变得更加稳定.可用 ...

  10. Android -- 使用主题配置文件,去掉程序启动界面的短暂黑屏

    关于黑屏 默认的情况下,程序启动时,会有一个黑屏的时期,原因是,首个activity会加载一些数据,比如初始化列表数据等. 去除步骤 1./res/values/styles.xml 在 Theme ...