感谢大量宝贵的资源,我得到了一些关于在AngularJS应用程序中实现组件的通用建议:
控制器
控制器应该只是模型和视图之间的中间层。尽可能使其轻量化。
强烈建议在控制器中避免业务逻辑。应该将其移动到模型中。
控制器可以使用方法调用(当子级想要与父级通信时)或$emit、$broadcast和$on方法与其他控制器通信。发出和广播的消息应最小化。
控制器不应该关心演示或DOM操作。
尽量避免嵌套控制器。在这种情况下,父控制器被解释为模型。而应该注入作为共享服务的模型。
控制器中的作用域应该用于将模型与视图进行绑定和封装视图模型,以实现表示模型设计模式。
作用域
在模板中,将作用域视为只读;在控制器中,将其视为只写。作用域的目的是引用模型,而不是成为模型。
进行双向绑定(ng-model)时,请确保不直接绑定到作用域属性。
模型
在AngularJS中,模型是由服务定义的单例。
模型提供了一种优秀的分离数据和显示的方式。
模型是单元测试的主要候选对象,因为它们通常具有一个依赖项(某种形式的事件发射器,在常见情况下是$rootScope),并包含高度可测试的领域逻辑。
模型应被视为特定单元的实现。它基于单一责任原则。单元是一个实例,负责其自身范围内的相关逻辑,可以在编程世界中以数据和状态的形式表示现实世界中的单个实体。
模型应该封装应用程序的数据,并提供访问和操作该数据的 API。
模型应该是可移植的,因此可以轻松地传输到类似的应用程序。
通过将单元逻辑隔离在模型中,您已经使其更易于定位、更新和维护。
模型可以使用更通用的全局模型的方法,这些方法对整个应用程序都是常见的。
如果不是真正依赖于减少组件耦合并增加单元测试性和可用性,请避免将其他模型组合到您的模型中使用依赖注入。
请避免在模型中使用事件监听器。它会使它们更难以测试,并且通常会破坏单一责任原则。
模型实现
由于模型应该在数据和状态方面封装一些逻辑,因此在架构上应限制对其成员的访问,从而可以保证松耦合。
在 AngularJS 应用程序中定义模型的方法是使用工厂服务类型进行定义。这将使我们能够非常轻松地定义私有属性和方法,并在单个位置返回公共可访问的属性和方法,这将使开发人员真正易读。
示例:
angular.module('search')
.factory( 'searchModel', ['searchResource', function (searchResource) {
var itemsPerPage = 10,
currentPage = 1,
totalPages = 0,
allLoaded = false,
searchQuery;
function init(params) {
itemsPerPage = params.itemsPerPage || itemsPerPage;
searchQuery = params.substring || searchQuery;
}
function findItems(page, queryParams) {
searchQuery = queryParams.substring || searchQuery;
return searchResource.fetch(searchQuery, page, itemsPerPage).then( function (results) {
totalPages = results.totalPages;
currentPage = results.currentPage;
allLoaded = totalPages <= currentPage;
return results.list
});
}
function findNext() {
return findItems(currentPage + 1);
}
function isAllLoaded() {
return allLoaded;
}
return {
init: init,
find: findItems,
allLoaded: isAllLoaded,
findNext: findNext
};
});
创建新实例
尽量避免使用返回新函数的工厂,因为这会破坏依赖注入,使库的行为变得尴尬,特别是对于第三方。
完成相同任务的更好方法是将工厂用作 API,返回附有 getter 和 setter 方法的对象集合。
angular.module('car')
.factory( 'carModel', ['carResource', function (carResource) {
function Car(data) {
angular.extend(this, data);
}
Car.prototype = {
save: function () {
var carData =
return carResource.save(carData);
}
};
function getCarById ( id ) {
return carResource.getById(id).then(function (data) {
return new Car(data);
});
}
return {
findById: getCarById
};
});
全局模型
通常情况下,尽量避免这种情况并正确设计您的模型,以便可以将其注入到控制器中并在视图中使用。
特殊情况下,某些方法需要应用程序内的全局访问权限。为了实现这一点,您可以在应用程序引导期间在$rootScope中定义“common”属性,并将其绑定到commonModel:
angular.module('app', ['app.common'])
.config(...)
.run(['$rootScope', 'commonModel', function ($rootScope, commonModel) {
$rootScope.common = 'commonModel';
}]);
所有的全局方法都将位于“common”属性内。这是某种命名空间。
但不要在$rootScope中直接定义任何方法。这可能会导致使用ngModel指令时出现意外行为,通常会使您的作用域混乱并导致作用域方法被覆盖的问题。
资源
资源让您与不同的数据源交互。
应该使用单一职责原则(single-responsibility-principle)进行实现。
在特定情况下,它是一个可重复使用的代理,用于HTTP/JSON端点。
资源被注入到模型中,并提供发送/检索数据的可能性。
资源实现
创建资源对象的工厂,允许您与RESTful服务器端数据源进行交互。
返回的资源对象具有操作方法,提供高级行为而无需与低级别的$http服务进行交互。
服务
模型和资源都是服务。
服务是松耦合的、不相关的功能单元,它们是自包含的。
服务是 Angular 将客户端 Web 应用程序从服务器端带来的一个特性,在服务器端,服务一直是常用的。
Angular 提供了不同类型的服务。每个服务都有自己独特的用例。请阅读理解服务类型获取详细信息。
尽量考虑应用程序中的服务体系结构主要原则。
通常来说,根据Web 服务词汇表:
服务是表示在提供者实体和请求者实体的角度上形成一致功能的任务的能力的抽象资源。为了使用,服务必须由一个具体的提供者代理实现。
客户端结构
通常情况下,应用程序的客户端被分成模块。每个模块应该作为一个单元进行可测试。
尝试根据功能/特性或视图定义模块,而不是按类型定义。请参见Misko的演示获取详细信息。
模块组件可以按照控制器、模型、视图、过滤器、指令等类型进行传统分组。
但是模块本身是可重用的、可转移的和可测试的。
开发人员也更容易找到代码的某些部分及其所有依赖项。
详细信息请参见《大型AngularJS和JavaScript应用程序中的代码组织》。
文件夹结构示例:
|-- src/
| |-- app/
| | |-- app.js
| | |-- home/
| | | |-- home.js
| | | |-- homeCtrl.js
| | | |-- home.spec.js
| | | |-- home.tpl.html
| | | |-- home.less
| | |-- user/
| | | |-- user.js
| | | |-- userCtrl.js
| | | |-- userModel.js
| | | |-- userResource.js
| | | |-- user.spec.js
| | | |-- user.tpl.html
| | | |-- user.less
| | | |-- create/
| | | | |-- create.js
| | | | |-- createCtrl.js
| | | | |-- create.tpl.html
| |-- common/
| | |-- authentication/
| | | |-- authentication.js
| | | |-- authenticationModel.js
| | | |-- authenticationService.js
| |-- assets/
| | |-- images/
| | | |-- logo.png
| | | |-- user/
| | | | |-- user-icon.png
| | | | |-- user-default-avatar.png
| |-- index.html
一个良好的angular应用程序结构示例是由angular-app实现的 - https://github.com/angular-app/angular-app/tree/master/client/src
这也被现代应用程序生成器考虑 - https://github.com/yeoman/generator-angular/issues/109