angular风格指南
此文章内容包括官网中的风格指南,及知乎野草文章中关于Angular: Best Practices的总结。
学习angular代码风格及最佳实践能写出更漂亮的程序。
1 单一职责
对所有的组件、服务等等应用单一职责原则 (SRP)。这样可以让应用更干净、更易读、更易维护、更易测试
1.1 单一规则
- 坚持每个文件只定义一样东西(例如服务或组件)
- 考虑把文件大小限制在 400 行代码以内。
1.2 小函数
- 坚持定义简单函数
- 考虑限制在 75 行之内。
2 命名
2.1 总体命名原则
- 坚持所有符号使用一致的命名规则。
- 坚持遵循同一个模式来描述符号的特性和类型。推荐的模式为 feature.type.ts。
例如,app/heroes/hero-list.component.ts 包含了一个用来管理英雄列表的组件
2.2 使用点和横杠来分隔文件名
- 坚持 在描述性名字中,用横杠来分隔单词。
- 坚持使用点来分隔描述性名字和类型。
- 坚持遵循先描述组件特性,再描述它的类型的模式,对所有组件使用一致的类型命名规则。推荐的模式为 feature.type.ts。
- 坚持使用惯用的后缀来描述类型,包括 *.service、*.component、*.pipe、.module、.directive。 必要时可以创建更多类型名,但必须注意,不要创建太多。
像 .service 这样的没有简写过的类型名字,描述清楚,毫不含糊。 像 .srv, .svc, 和 .serv 这样的简写可能令人困惑。
2.3 符号名与文件名
- 坚持为所有东西使用一致的命名约定,以它们所代表的东西命名。
- 坚持使用大写驼峰命名法来命名类。符号名匹配它所在的文件名。
- 坚持在符号名后面追加约定的类型后缀(例如 Component、Directive、Module、Pipe、Service)。
- 坚持在文件名后面追加约定的类型后缀(例如 .component.ts、.directive.ts、.module.ts、.pipe.ts、.service.ts)。
如符号名:export class HeroesComponent{}
文件名:heroes.component.ts
2.4 引导
- 坚持把应用的引导程序和平台相关的逻辑放到名为 main.ts 的文件里。
- 坚持在引导逻辑中包含错误处理代码。
- 避免把应用逻辑放在 main.ts 中,而应放在组件或服务里。
1
2
3
4
5
6
7import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.then(success => console.log(`Bootstrap success`))
.catch(err => console.error(err));
2.5 组件选择器
- 坚持使用中线命名法(dashed-case)或叫烤串命名法(kebab-case)来命名组件的元素选择器。
1
2
3
4
5
6
7
8
9
10
11
12
({
selector: 'tohHeroButton',/****** avoid ******/
templateUrl: './hero-button.component.html'
})
export class HeroButtonComponent {}
({
selector: 'toh-hero-button',/****** right ******/
templateUrl: './hero-button.component.html'
})
export class HeroButtonComponent {}
2.6 为组件添加自定义前缀
- 坚持使用带连字符的小写元素选择器值(例如 admin-users)。
- 坚持为组件选择器添加自定义前缀。 例如,toh 前缀表示 Tour of Heroes(英雄指南),而前缀 `admin 表示管理特性区。
- 坚持使用前缀来识别特性区或者应用程序本身。
- 防止与其它应用中的组件和原生 HTML 元素发生命名冲突。
- 更容易在其它应用中推广和共享组件。
- 组件在 DOM 中更容易被区分出来
错误示例:
1 | /* avoid */ |
1 | /* avoid */ |
正确示例:
1 | ({ |
1 | ({ |
2.7 指令选择器
- 坚持使用小驼峰形式命名指令的选择器。
2.8 为指令添加自定义前缀
- 坚持为指令的选择器添加自定义前缀(例如前缀 toh 来自 Tour of Heroes)。
- 坚持用小驼峰形式拼写非元素选择器,除非该选择器用于匹配原生 HTML 属性。
错误示例:
1 | /* avoid */ |
正确示例:
1 | @Directive({ |
2.9 管道名
- 坚持为所有管道使用一致的命名约定,用它们的特性来命名。
1
2
3
4
5
6// 文件名:
elipsis.pipe.ts
// 符号名:
name: 'ellipsis' }) ({
export class EllipsisPipe implements PipeTransform { }
1 | // 文件名: |
3 编程约定
3.1 常量
- 坚持用 const 声明变量,除非它们的值在应用的生命周期内会发生变化。
4 应用程序结构与NgModule
所有应用程序的源代码都放到名叫src
的目录里。所有特性区都在自己的文件夹中,带有它们自己的NgModule。
所有内容都遵循每个文件一个特性的原则。每个组件、服务和管道都在自己的文件里。所有第三方程序包保存到其它目录里,而不是src
目录。
4.1 LIFT
- 坚持组织应用的结构,力求:
- (Locate)快速定位 代码
- (Identify)一眼识别 代码
- (Flattest)尽量保持扁平结构
- (Try)尝试遵循 DRY (Do Not Repeat Yourself, 不重复自己) 原则
- 坚持四项基本原则定义文件结构,上面的原则是按重要顺序排列的。
检查应用结构是否合理的方法是问问自己:我能快速打开与此特性有关的所有文件并开始工作吗?
4.2 定位
坚持直观、简单和快速地定位代码
要想高效的工作,就必须能迅速找到文件,特别是当不知道(或不记得)文件名时。 把相关的文件一起放在一个直观的位置可以节省时间。 富有描述性的目录结构会让你和后面的维护者眼前一亮。
4.3 识别
- 坚持命名文件到这个程度:看到名字立刻知道它包含了什么,代表了什么。
- 坚持文件名要具有说明性,确保文件中只包含一个组件。
- 避免创建包含多个组件、服务或者混合体的文件
当你有一组小型、紧密相关的特性时,违反一物一文件的规则可能会更好, 这种情况下单一文件可能会比多个文件更容易发现和理解。
4.4 扁平
- 坚持尽可能保持扁平的目录结构。
- 考虑当同一目录下达到 7 个或更多个文件时创建子目录。
- 考虑配置 IDE,以隐藏无关的文件,例如生成出来的 .js 文件和 .js.map 文件等。
4.5 T-DRY(尽量不重复自己)
- 坚持 DRY(Don’t Repeat Yourself,不重复自己)。
- 避免过度 DRY,以致牺牲了阅读性。
虽然 DRY 很重要,但如果要以牺牲 LIFT 的其它原则为代价,那就不值得了。 这也就是为什么它被称为 T-DRY。 例如,把组件命名为 hero-view.component.html 是多余的,因为带有 .html 扩展名的文件显然就是一个视图 (view)。 但如果它不那么显著,或不符合常规,就把它写出来。
4.6 共享特性模块
- 坚持在 shared 目录中创建名叫 SharedModule 的特性模块(例如在 app/shared/shared.module.ts 中定义 SharedModule)。
- 坚持在共享模块中声明那些可能被特性模块引用的可复用组件、指令和管道。
- 考虑把可能在整个应用中到处引用的模块命名为 SharedModule
- 考虑不要在共享模块中提供服务。服务通常是单例的,应该在整个应用或一个特定的特性模块中只有一份。 不过也有例外,比如,在下面的范例代码中,注意 SharedModule 提供了 FilterTextService。这里可以这么做,因为该服务是无状态的,也就是说,该服务的消费者不会受到这些新实例的影响。
- 坚持在 SharedModule 中导入所有模块都需要的资产(例如 CommonModule 和 FormsModule)。
- 坚持在 SharedModule 中声明所有组件、指令和管道。
- 坚持从 SharedModule 中导出其它特性模块所需的全部符号。
- 避免在 SharedModule 中指定应用级的单例服务提供商。如果是刻意要得到多个服务单例也行,不过还是要小心。
4.7 核心特性模块
TODO