1 Angular 语言规则
使用 Closure 的 goog.require 和 goog.provide 管理依赖
为您的项目选择一个命名空间,并使用 goog.provide 和 goog.require。
goog.provide('hello.about.AboutCtrl');
goog.provide('hello.versions.Versions');为什么? Google BUILD 规则与 Closure 的 provide/require 能很好地集成。
模块(Module)
您的主应用模块应该位于客户端根目录中。模块只能在其定义所在的位置被修改,不应在其他地方修改。
模块可以与其组件定义在同一文件中(这适用于只包含一个服务的模块),也可以在单独的文件中将各部分组合在一起。
为什么? 模块对于任何想将其作为可重用组件引入的人来说应该是一致的。如果一个模块的含义取决于包含了哪些文件,那它就不是一致的。
模块应使用 Angular Module 的 “name” 属性引用其他模块
例如:
// 文件 submodulea.js:
goog.provide('my.submoduleA');
my.submoduleA = angular.module('my.submoduleA', []);
// ...
// 文件 app.js
goog.require('my.submoduleA');
Yes: my.application.module = angular.module('hello', [my.submoduleA.name]);
No: my.application.module = angular.module('hello', ['my.submoduleA']);
为什么? 使用 my.submoduleA 的属性可以避免 Closure 预提交检查报错(提示文件被引入但从未使用)。使用 .name 属性可以避免重复字符串。
使用通用的 externs 文件
这能最大程度地让 JS 编译器在存在 Angular 外部提供的类型时执行类型安全检查,并且意味着您不必担心 Angular 变量被以令人困惑的方式混淆。
Google 外部读者请注意:当前的 externs 文件位于 Google 内部目录中,但可以在 GitHub 上找到一个示例 这里 。
JSCompiler 标志
提醒:根据 JS 风格指南,面向用户的代码必须进行编译。
推荐:使用 JSCompiler(默认与 js_binary 配合使用的 Closure 编译器)并使用 //javascript/angular/build_defs/build_defs 中的 ANGULAR_COMPILER_FLAGS_FULL 作为基础标志。
注意——如果您对方法使用了 @export,则需要添加编译器标志:
"--generate_exports",如果您对属性使用了 @export,则需要添加以下标志:
"--generate_exports",
"--remove_unused_prototype_props_in_externs=false",
"--export_local_property_definitions",控制器(Controller)和作用域(Scope)
控制器是类。方法应该定义在 MyCtrl.prototype 上。
Google 的 Angular 应用应使用 ‘controller as’ 风格将控制器导出到作用域上。这在 Angular 1.2 中已完全实现,在 Angular 1.2 之前的版本中也可以模拟实现。
在 Angular 1.2 之前,写法如下:
/**
* Home 控制器。
*
* @param {!angular.Scope} $scope
* @constructor
* @ngInject
* @export
*/
hello.mainpage.HomeCtrl = function($scope) {
/** @export */
$scope.homeCtrl = this; // 这是在 Angular 1.2 controller-as 之前的过渡方案
/**
* @type {string}
* @export
*/
this.myColor = 'blue';
};
/**
* @param {number} a
* @param {number} b
* @export
*/
hello.mainpage.HomeCtrl.prototype.add = function(a, b) {
return a + b;
};对应的模板:
<div ng-controller="HomeCtrl">
<span ng-style="homeCtrl.myColor">I'm in a color!</span>
{{homeCtrl.add(5, 6)}}
</div>在 Angular 1.2 之后,写法如下:
/**
* Home 控制器。
*
* @constructor
* @ngInject
* @export
*/
hello.mainpage.HomeCtrl = function() {
/**
* @type {string}
* @export
*/
this.myColor = 'blue';
};
/**
* @param {number} a
* @param {number} b
* @export
*/
hello.mainpage.HomeCtrl.prototype.add = function(a, b) {
return a + b;
};如果您使用属性重命名进行编译,请使用 @export
注解来暴露属性和方法。同时也要记得 @export 构造函数。
在模板中:
<div ng-controller="HomeCtrl as homeCtrl">
<span ng-style="homeCtrl.myColor">I'm in a color!</span>
{{homeCtrl.add(5, 6)}}
</div>为什么? 将方法和属性直接放在控制器上,而不是构建一个作用域对象,更符合 Google Closure 的类风格。此外,使用 ‘controller as’ 可以在多个控制器作用于同一元素时,清楚地知道您正在访问哪个控制器。由于绑定中始终有一个 ’.’,您不必担心原型继承遮蔽基本类型的问题。
指令(Directive)
所有 DOM 操作都应在指令内完成。指令应保持小巧并使用组合模式。定义指令的文件应通过 goog.provide 提供一个返回指令定义对象(Directive Definition Object)的静态函数。
goog.provide('hello.pane.paneDirective');
/**
* 描述和用法
* @return {angular.Directive} 指令定义对象。
*/
hello.pane.paneDirective = function() {
// ...
};例外:对于与视图其余部分断开的 DOM 元素(如对话框或键盘快捷键),可以在服务(Service)中进行 DOM 操作。
服务(Service)
通过 module.service
在模块上注册的服务是类。除非您需要在创建类的新实例之外进行额外的初始化,否则请使用
module.service 而不是 module.provider 或 module.factory。
/**
* @param {!angular.$http} $http Angular 的 http 服务。
* @constructor
*/
hello.request.Request = function($http) {
/** @type {!angular.$http} */
this.http_ = $http;
};
hello.request.Request.prototype.get = function() {/*...*/};在模块中:
module.service('request', hello.request.Request);