Angular2 开发规范
目录把所有应用程序的源代码都放到名叫 app 的目录里。所有内容都遵循每个文件单个特性的原则。每个组件、服务和管道都在自己的文件里。所有第三方程序包都被保存到其它目录里而不在 app 目录里,我们不会修改它们,所以不希望它们弄乱我们的应用程序。使用本指南介绍的文件命名约定。 尽可能保持平面化的目录结构当有 7个或更多文件时才新建目录; 为每个组件新建一个目录,保存它的 .ts,.html,.css,.spec 等文件; 在单个特性范围内,把所有共享的文件放到 shared 目录,在一个特性范围内,分离出组件共享的文件; 把目录的名字命名为它包含的特性名字; 布局组件把定义总体布局的组件放到 shared 目录; 把共享的布局组件放到 shared 目录下它自己的独立目录里; 创建和导入封装桶新建一个文件,用来导入、归集和重新导出项目。该技巧被称作 封装桶 。把该封装桶文件命名为 index.ts ; 为惰性加载目录名字加前缀 +,使用组件路由器来惰性加载可以路由的特性。 命名文件到这个程度:可以从名字立刻知道它包含了什么,代表了什么; 文件名要具有说明性,并保证文件中只包含一个组件; 避免 创建包含很多组件、服务或者混合体的文件; 把模板和样式提取到它们自己的文件当超过三行的时候,把模板和样式提取到一个单独的文件 当组件名字为 [component-name] 的时候,命名它的模板为 [component-name].component.html 文件结构如下图所示: 文件每个文件只定义一样东西 (比如服务或者组件),把文件大小限制在 400行代码以内; 定义小函数,限制在 75 行之内; 遵循同一个模式来描述符号的特性和类型。推荐的模式为 feature.type.ts,使用惯用的后缀来描述类型,比如 *.service 、 *.component 、 *.pipe、*.model; 在描述性名字里面,使用横杠来分隔单词; 启动把应用的引导程序和平台相关的逻辑放到名为 避免把应用逻辑放到 组件与指令命名以它们所代表的东西命名; 使用大写驼峰命名法来命名所有符号 ( 类 ) 。保持符号的名字与它所在的文件名字相同; 把符号的类型 ( 比如组件、服务、指令等 ) 附加到符号名的后面; 如下所示:
服务使用大写驼峰命名法来命名服务; 当不能从它们的名字里清楚的看出它们是什么的时候 ( 比如它们的名字是名词时 ) ,添加 Service 后缀; 如下所示:
指令的选择器使用小驼峰命名法来命名指令的选择器,Angular 2 HTML 解析器是大小写敏感的,它识别小写驼峰写法; 保持指令里定义的属性名字与它们绑定的视图 HTML 属性名字一致; 为组件的选择器使用自定义前缀。比如,前缀 tod 是从 Tour of Heros 来的,前缀 admin 代表了 admin 的特性区域; 使用前缀来识别特性区域或者应用程序本身; 如下所示 @Component({ selector: 'admin-users' }) export class UsersComponent {} @Directive({ selector: '[tohValidate]' }) export class ValidateDirective {} 管道名为所有管道使用前后一致的命名约定,用它们的特性来命名; 如下: @Pipe({ name: 'ellipsis' }) export class EllipsisPipe implementsPipeTransform { } 编程约定类使用大写驼峰命名法来命名类; export class ExceptionService { constructor() { } } 常量用 const 声明变量,除非它们的值在应用的生命周期内会发生变化; 把常量名拼写为小驼峰格式; export const mockHeroes = ['Sam','Jill']; 接口使用大写驼峰命名法来命名接口; 不要在接口名字前面加 I 前缀; 属性和方法使用小写驼峰命名法来命名属性和方法; 避免使用下划线为前缀来命名私有属性和方法; import { Injectable } from '@angular/core'; @Injectable() export class ToastService { message: string; privatetoastCount: number; hide() { this.toastCount--; this.log(); } show() { this.toastCount++; this.log(); } private log() { console.log(this.message); } } 内联 Input 和 Output 属性装饰器使用 @Input 和 @Output,而非 @Directive 和 @Component 装饰器里面的 inputs 和 outputs 属性; 把 @Input() 或者 @Output() 放到它们装饰的属性的同一行; 成员顺序把属性成员放到顶部,方法成员紧随其后; 先放公共成员,再放私有成员,并按照字母顺序排列; export class ToastComponent implementsOnInit { //public properties message: string; title: string; //private fields privatedefaults = { title: '', message: 'May the Force be with You' }; private toastElement: any; //public methods activate(message = this.defaults.message,title = this.defaults.title) { this.title = title; this.message = message; this.show(); } ngOnInit() { this.toastElement = document.getElementById('toh-toast'); } //private methods private hide() { this.toastElement.style.opacity = 0; window.setTimeout(() => this.toastElement.style.zIndex = 0,400); } private show() { console.log(this.message); this.toastElement.style.opacity = 1; this.toastElement.style.zIndex = 9999; window.setTimeout(() => this.hide(),2500); } } 把逻辑放到服务里把组件类中的逻辑限制到只有视图需要的逻辑。所有其它逻辑都应该被放到服务; 把可以重复使用的逻辑放到服务里,保持组件简单并聚焦于它们预期目的; 错误的方式 /* avoid */ import { OnInit } from '@angular/core'; import { Http,Response } from'@angular/http'; import { Observable } from'rxjs/Observable'; import { Hero } from'../shared/hero.model'; const heroesUrl = 'http://angular.io'; export class HeroListComponent implementsOnInit { heroes: Hero[]; constructor(private http: Http) {} getHeroes() { this.heroes = []; this.http.get(heroesUrl) .map((response: Response) => <Hero[]>response.json().data) .catch(this.catchBadResponse) .finally(() => this.hideSpinner()) .subscribe((heroes: Hero[]) => this.heroes = heroes); } ngOnInit() { this.getHeroes(); } private catchBadResponse(err: any,source: Observable<any>) { // log and handle the exception return new Observable(); } private hideSpinner() { // hide the spinner } } 正确的方式 import { Component,OnInit } from'@angular/core'; import { Hero,HeroService } from'../shared'; @Component({ selector: 'toh-hero-list', template: `...` }) export class HeroListComponent implementsOnInit { heroes: Hero[]; constructor(private heroService: HeroService) {} getHeroes() { this.heroes = []; this.heroService.getHeroes() .subscribe(heroes => this.heroes = heroes); } ngOnInit() { this.getHeroes(); } } 不要给输出属性加前缀命名事件时,不要带前缀 on; 命名事件处理方法时,带前缀 on ,紧跟事件名字; export class HeroComponent { @Output() savedTheDay = new EventEmitter<boolean>(); } 使用指令来加强已有元素当你需要有无模板的展示逻辑时,使用属性型指令。 @Directive({ selector: '[tohHighlight]' }) export class HighlightDirective { @HostListener('mouSEOver') onMouseEnter() { // do highlight work } } <divtohHighlight>Bombasta</div> 使用 HostListener 和 HostBinding 类装饰器使用 @HostListener 和 @HostBinding ,而非 @Directive 和 @Component 装饰器的宿主属性。与属性或方法名字相关联的@HostBinding 或者 @HostListener 应该只在一个地方被修改:在指令的类里。反过来,如果我们使用宿主属性,我们需要在控制器内修改属性声明,然后在指令相关的元数据里修改 @Directive({ selector: '[tohValidator]' }) export class ValidatorDirective { @HostBinding('attr.role') role = 'button'; @HostListener('mouseenter') onMouseEnter() { // do work } } 服务在同一个注入器内,服务是单例在同一个注入器内,把服务当做单例使用。使用它们来共享数据和功能。 export class HeroService { constructor(private http: Http) { } getHeroes() { return this.http.get('api/heroes') .map((response: Response) => <Hero[]>response.json().data); } } 单一职责新建单一职责的服务,把它封装在自己的环境内; 当服务成长到超出单一用途时,新建一个服务; 提供一个服务在被共享范围内的顶级组件里,将服务提供到 Angular 2 的注入器里; Angular 2 注入器是层次性的。在顶层组件提供服务时,该服务实例在所有该顶级组件的子级组件中可见并共享。 当不同的两个组件需要一个服务的不同的实例时,上面的方法这就不理想了。在这种情况下,我们最好在需要崭新和单独服务实例的组件里提供服务 使用 @Injectable() 类装饰器当使用类型作为令牌来注入服务的依赖时,使用 @Injectable 类装饰器,而非 @Inject 参数装饰器。 Angular 的依赖注入机制,是根据在服务的构造函数里面的类型的声明,来解析所有服务的依赖的。 数据服务分离数据调用把数据操作和数据互动重构到服务里; 让数据服务来负责 XHR 调用、本地储存、内存储存或者其他数据操作; 生命周期钩子实现生命周期钩子接口实现生命周期钩子接口,避免在方法名字拼写错误时,造成无意间没有调用钩子的可能。 @Component({ selector: 'toh-hero-button', template: `<button>OK</button>` }) export class HeroButtonComponent implementsOnInit { ngOnInit() { console.log('The component is initialized'); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |