架构概况
Angular是一个框架!它包含很多东西,诸如:数据绑定、路由、动画、表单等,所有这些东西很多不是必选的,你可以按需索取。
一个Angular应用是由一个复杂的组件树组成,组件管理HTML模板渲染、HTML事件与组件类互动,而Service服务处理业务逻辑,最后组件与服务都由DI(依赖注入)负责管理,最后由模块来引导应用启动。
以下是一个概况图。

这张图包含下列概念:
- Moduel
- Component
- Template
- Data binding
- Metadata
- Directive
- Service
- Dependency injection
以下会对每一个概念进行简单的介绍。
Module
一个应用会有成百上千组件,这么多组件,不可能是一下子全部用得到的呀,那要怎么维护呢。
比如一个登录功能,可能只需要登录组件、注册组件、忘记密码组件、登录API接口服务、登录状态存储而已呀,我们把这些组件汇聚在一起,然后叫它为模块(Module)。
Angular框架使用 NgModule
来表述,大概长这样:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ UserService ],
declarations: [ Login, Register, Froget ],
exports: [ UserService ]
})
export class UserModule { }
这里有几个属性:
imports
导入别人的组件,比如可能需要一个发送短信获取验证码的功能。declarations
注册登录、注册、忘记密码组件providers
注册API接口服务exports
如果觉得某个组件或服务要给另一个模块用的,那就导出它
你是不是和我一样觉得,模块可以让一个业务的功能块更内聚;没错,这就是模块的作用。
Component
组件,我觉得应该叫视图组件更合理一点,因为一个组件必须对应一个模板,使用 @Component
装饰器。
@Component({
template: `
<p>当前用户:{{ name }}</p>
<button (click)="login()">登录</button>
`
})
export class UserLoginComponent {
name: string = 'cipchk';
login() {
// 登录动作
}
}
示例中模板(template)需要组件类(UserLoginComponent)提供数据(name),而登录动作(login)需要模板(button.click)的触发。彼此之间就是相互作用的。
组件的创建、销毁都是由Angular来管理,当然了,Angular提供一系列生命周期钩子让我们在某个阶段有机会做点处理。
Template
模板就是视图,几乎所有HTML语法都是有效的模板语法,这一点归于Angular模板引擎。
注:Angular与AngularJS的模板引擎有很大的区别,我相信有一些人却步AngularJS就是因为需要你了解一大堆
ng-
开头的指令。然,Angular做了极大的改进,你再也看不到ng-style
之类的,换之尽可能保留HTML5标签。比如:[style]
直接表示样式属性。
Data binding
数据绑定,Angular提供了非常丰富的模板语法,诸如:*ngFor
、{{ name }}
、(click)
、[(ngModel)]
等。
Metadata
Angular冲刺大量元数据,不管是组件、指令、模块等等都离不开它。
元数据的目的是为目标添加特定约束——目标可能是组件、指令、模块等。
比如限定组件只能使用 <login></login>
调用。
@Component({
selector: 'login'
})
Directive
指令就是一个无模板组件,使用 @Directive
装饰器。
Angular提供丰富的内置指令,但分成结构型和属性型两类。
结构型
会改变 DOM 结构的指令,例如:*ngFor
、*ngIf
、*ngSwitch
。
属性型
会改变 DOM 元素的外观或行为,例如:[ngClass]
、[ngStyle]
及 HTML5 标签属性([src]
、[href]
、(click)
)。
Service
服务类就是一个简单的 Class 类;主要作用是用于业务逻辑、HTTP通信、数据存储等,从而可以隔离组件的业务逻辑。
那为什么要这么做,因为好像很麻烦呀。
- 从测试角度出发呢?测试一个 Class 比测试一个组件简单得多了,那为何不这么做?
- 组件会在需要的时候才被实例,销毁后数据会丢失。
- 服务是单例对象,仅会实例一次,在整个应用有效。
Dependency injection
依赖注入,这是整个Angular框架的核心,同软件工程的DI一样,DI最大的目的是减少依赖关系、易测试。
class UserService {
constructor(private service: ApiService) {}
}
如果某个组件需要 UserService
的话,并不需要再构建一个 ApiService
。
class LoginComponent {
constructor(private userService: UserService) {}
}
而对于测试,可以直接模拟一个 ApiService
,这样的测试代码更有效,也不会因为其依赖导致我们测试代码更为复杂。