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;

在描述性名字里面,使用横杠来分隔单词;

启动

把应用的引导程序和平台相关的逻辑放到名为main.ts文件里;

避免把应用逻辑放到main.ts里。考虑把它放到组件或服务里面

组件与指令命名

以它们所代表的东西命名;

使用大写驼峰命名法来命名所有符号 ( 类 ) 。保持符号的名字与它所在的文件名字相同;

把符号的类型 ( 比如组件、服务、指令等 ) 附加到符号名的后面;

如下所示:

符号名

文件

export class HeroesComponent

heroes.component.ts

export class HeroListComponent

hero-list.component.ts

export class ValidationDirective

validation.directive.ts

服务

使用大写驼峰命名法来命名服务;

当不能从它们的名字里清楚的看出它们是什么的时候 ( 比如它们的名字是名词时 ) ,添加 Service 后缀;

如下所示:

符号名

文件

export class HeroDataService {}

hero-data.service.ts

export class CreditService {}

credit.service.ts

指令的选择器

使用小驼峰命名法来命名指令的选择器,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');

}

}

相关文章

ANGULAR.JS:NG-SELECTANDNG-OPTIONSPS:其实看英文文档比看中...
AngularJS中使用Chart.js制折线图与饼图实例  Chart.js 是...
IE浏览器兼容性后续前言 继续尝试解决IE浏览器兼容性问题,...
Angular实现下拉菜单多选写这篇文章时,引用文章地址如下:h...
在AngularJS应用中集成科大讯飞语音输入功能前言 根据项目...
Angular数据更新不及时问题探讨前言 在修复控制角标正确变...