Angular规范

时间:2023-03-09 18:17:10
Angular规范

 只记录一些自己未曾用过,但觉得对以后的项目有帮助的规范

一 Javascript闭包

把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() { }
})();

二 Modules

2.1 定义

  不使用任何一个使用了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'
]);

 2.2 Getters

  使用module的时候,避免直接用一个变量,而是使用getter的链式语法。

  为什么?:这将产生更加易读的代码,并且可以避免变量冲突和泄漏。

/* avoid */
var app = angular.module('app');
app.controller('SomeController', SomeController); function SomeController() { }
/* recommended */
angular
.module('app')
.controller('SomeController', SomeController); function SomeController() { }

 2.3 Setting vs Getting

  只能设置一次。

  为什么?:一个module只能被创建一次,创建之后才能被检索到。

  • 设置module,angular.module('app', []);
  • 获取module,angular.module('app');

 2.4命名函数 vs 匿名函数

  回调函数使用命名函数,不要用匿名函数。

  为什么?:易读,方便调试,减少嵌套回调函数的数量。

/* 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

 3.1 controllerAs在View中的语法

使用controllerAs 语法代替直接用经典的$scope定义的controller的方式。

为什么?:congtroller被构建的时候,就会有一个新的实例,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>

 3.2 controllerAs在controller中的语法

  • 使用 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() { };
}

  3.3 controllerAs with vm

  使用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() { };
}

  

3.4 可绑定成员放到顶部

把可绑定的成员放到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() {
/* */
}

  Angular规范

3.5 把Controller中的逻辑延迟到Service中

通过委派到service和factory中来延迟controller中的逻辑。

为什么?:把逻辑放到service中,并通过一个function暴露,就可以被多个controller重用。

为什么?:把逻辑放到service中将会使单元测试的时候更加容易地把它们分离,相反,如果在controller中调用逻辑就显得很二了。

为什么?:保持controller的简洁。

为什么?:从controller中删除依赖关系并且隐藏实现细节。

四 Factory

4.1 单一职责

factory应该是单一职责,这是由其上下文进行封装的。一旦一个factory将要处理超过单一的目的时,就应该创建一个新的factory

4.2 可访问的成员放到顶部

使用从显露模块模式派生出来的技术把service(它的接口)中可调用的成员暴露到顶部,

为什么?:易读,并且让你可以立即识别service中的哪些成员可以被调用,哪些成员必须进行单元测试(或者被别人嘲笑)。

为什么?:当文件内容很长时,这可以避免需要滚动才能看到暴露了哪些东西。

为什么?:虽然你可以随意写一个函数,但当函数代码超过一行时就会降低可读性并造成滚动。通过把实现细节放下面、可调用接口放到顶部的返回service的方式来定义可调用的接口,从而使代码更加易读。

/* avoid */
function dataService () {
var someValue = '';
function save () {
/* */
};
function validate () {
/* */
}; return {
save: save,
someValue: someValue,
validate: validate
};
}

  

/* recommended */
function dataService () {
var someValue = '';
var service = {
save: save,
someValue: someValue,
validate: validate
};
return service; //////////// function save () {
/* */
}; function validate () {
/* */
};
}

  Angular规范

五 Data Services

5.1 独立的数据调用

把进行数据操作和数据交互的逻辑放到factory中,数据服务负责XHR请求、本地存储、内存存储和其它任何数据操作。

为什么?:controller的作用是查看视图和收集视图的信息,它不应该关心如何取得数据,只需要知道哪里需要用到数据。把取数据的逻辑放到数据服务中能够让controller更简单、更专注于对view的控制。

为什么?:方便测试。

为什么?:数据服务的实现可能有非常明确的代码来处理数据仓库,这可能包含headers、如何与数据交互或是其它service,例如$http。把逻辑封装到单独的数据服务中,这隐藏了外部调用者(例如controller)对数据的直接操作,这样更加容易执行变更。

六 Directives

6.1 一个dirctive一个文件

一个文件中只创建一个directive,并依照directive来命名文件。

为什么?:虽然把所有directive放到一个文件中很简单,但是当一些directive是跨应用的,一些是跨模块的,一些仅仅在一个模块中使用时,想把它们独立出来就非常困难了。

为什么?:一个文件一个directive也更加容易维护。

注: "最佳实践:Angular文档中有提过,directive应该自动回收,当directive被移除后,你可以使用element.on('$destroy', ...)或者scope.$on('$destroy', ...)来执行一个clearn-up函数。"