下面我个人的关于Angular2构造块学习笔记,其中大部分内容整理自Angular中文官网和《揭秘Angular2》这本书。
Angular 应用中有8个主要构造块:模块 (module)、组件 (component)、模板 (template)、元数据 (metadata)、数据绑定 (data binding)、指令 (directive)、服务 (service)和依赖注入 (dependency injection)。下文将简单介绍这八个构造块:
Angular 应用是模块化的,并且 Angular 有自己的模块系统,它被称为 Angular 模块或 NgModules。每个 Angular 应用至少有一个模块(根模块),习惯上命名为AppModule。根模块在一些小型应用中可能是唯一的模块,大多数应用会有很多特性模块。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent,
... ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
如上代码块所示:
1. 在使用Angular库的库模块之前首先要用import进行导入。
2. @NgModule是一个装饰器函数。Angular 模块都带有@NgModule装饰器。
3. imports属性用于导入其它模块,这里的其它模块是指我们在本模块使用的其它模块,可见,在Angular导入一个模块要先做操作1:用import进行导入,再做操作3在imports属性中导入,这才算成功引入了一个模块。
4. providers属性是服务的创建者,可以理解为服务的提供商。在模块中声明服务的提供商就是将其加入到全局服务列表中,使其服务可用于应用的任何部分(如模块中的其他组件)。
5. declarations属性用来声明本模块中拥有的视图类,即这个数组包含了所有由我们创建的并属于该应用模块的组件、管道和指令。
6. exports属性是declarations 的子集,声明了可用于其它模块的组件或管道或指令或模块。
7. 只有根模块才能设置bootstrap属性,用来指定应用的主视图(称为根组件)。
组件负责控制屏幕上的一小块区域,组件可以理解为一个类(组件类)。组件通过一些由属性和方法组成的 API 与视图进行交互。
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(private service: HeroService) { }
ngOnInit() {
this.heroes = this.service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
这里先解释生命周期钩子的概念:每个组件都有一个生命周期:新建、更新和销毁。生命周期钩子就是把这些关键生命时刻暴露出来,让开发者可以介入生命周期中的这些关键时刻。如上代码,这个组件类定义了heroes、selectedHero属性和selectHero()方法,通过ngOnInit()生命周期钩子来向这个组件注入HeroService服务从而获取数据。
我们通过组件的自带的模板(@Component的template属性)来定义组件视图。模板以HTML形式存在,告诉Angular如何渲染组件。
<ul>
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
{ {hero.name} }
li>
ul>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero">hero-detail>
如上代码,多数情况下,模板看起来很像标准 HTML,不同的是模板中会有一些指令(例如*ngFor
)、属性绑定(例如[hero]="selectedHero"
)、事件绑定(例如(click)="selectHero(hero)
)和一些其它组件定义的新元素(例如
标签)。
元数据告诉 Angular 如何处理一个类。上面提到的组件可以看到其实它就是一个类,我们用装饰器来连接元数据。如下代码,我们用装饰器@Component来附加元数据。
@Component({
//`moduleId: module.id,
selector: 'my-app',
template: `...
....
`
//templateUrl: './heroes.component.html',//方式二
styles: [`...`]
//styleUrls: [ './heroes.component.css' ]//方式二
的标签。
标签内;方式二:采用styleUrls属性进行链接,该属性是一个由样式文件的文件名(包括路径)组成的数组。我们还可以列出来自多个不同位置的样式文件。总结:当我们在父组件中嵌入
这个标签时,相当于将template这个html文件嵌入到该父组件中。
所谓数据绑定就是一种让模板的各部分与组件类的各部分相互合作的机制,可以理解为模板和组件类的桥梁。数据绑定方式有四种形式:
·插值表达式:
双大括号告诉应用从组件类中读取属性并渲染
·属性绑定:
·事件绑定:
·双向绑定:它使用ngModel指令组合了属性绑定和事件绑定的功能。在双向绑定中,数据属性值通过属性绑定从组件流到输入框。用户的修改通过事件绑定流回组件,把属性值设置为最新的值。
如上左图所示,模板通过事件绑定使用组件类的方法,模板通过属性绑定来使用组件类中的属性。如上右图所示,父组件通过属性绑定来使用子组件的属性,子组件通过事件绑定去改变父组件的属性值。可见,数据绑定不仅仅在模板与对应组件的交互中扮演了重要的角色,在父组件与子组件的通讯中也同样重要。
Angular模板是动态的。当Angular渲染它们时,它会根据指令提供的操作对DOM进行转换。严格来说组件就是一个指令,但是组件非常独特,且位于中心地位,所以我们把组件从指令中独立了出来。有两种类型的指令:结构型指令和属性型指令:
·结构型指令通过在 DOM 中添加、移除和替换元素来修改布局。例如:
·属性型指令修改一个现有元素的外观或行为。例如:
服务是一个广义范畴,包括:值、函数,或应用所需的特性。几乎任何东西都可以是一个服务。典型的服务是一个具有专注的、明确的用途的类。组件就是最大的服务消费者。
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
如上代码为一个用于把日志记录到浏览器的控制台的服务类的例子。
当Angular创建组件时,会首先为组件所需的服务请求一个注入器 (injector)。注入器是一个存放着以前创建的实例的容器。Angular通过查看组件类的构造函数的参数类型得知组件需要哪些服务。如果所请求的服务实例不在容器中,注入器就会创建去一个新的服务实例,并且添加到容器中,然后把这个服务返回给Angular。当所有请求的服务都被解析完并返回时Angular会以这些服务为参数去调用组件的构造函数,这就是依赖注入。
如上图所示,Angular将注入器的HeroService注入到组件中。所以创建了HeroService这个服务类之后,在Angular将服务注入组件之前,我们首先要标明HeroService的提供商。提供商就是用来创建或返回服务的,可以理解为服务的生产者。我们可以在模块中或组件中注册提供商,但通常会把提供商添加到根模块上,以便在应用的任何地方都可以使用服务的同一个实例。例如,我们在应用的根模块的@NgModule装饰器中添加服务提供商,这样模块的组件都可以使用这些服务实例。
providers: [
BackendService,
HeroService,
Logger
],
模块、组件、指令和服务之间的关系
对于一个Angular应用,我们可以这样描述:用 Angular 扩展语法编写 HTML模板(template),用组件类(例如AppComponent)管理这些模板,用服务添加应用逻辑,用模块打包发布组件与服务。下图描述了模块、组件、指令和服务之间的关系:
正如前面提到,每个 Angular 应用至少有一个根模块。例如,图片中的模块A是根模块,那模块A可以用@NgModule装饰器的imports属性导入其他模块(例如模块C),从而使用其它模块的组件类。而对于模块C而言,它就需要用@NgModule装饰器的exports属性导出该组件类。
换句话来理解根模块和其它模块之间的关系:根模块不需要导出任何东西,因为其它组件不需要导入根模块。我们通过引导根模块来启动该应用,在开发期间,我们通常在一个main.ts文件中引导AppModule:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
组件类应保持精简。组件本身不从服务器获得数据、不进行验证输入等等。这些任务要交给服务,组件的任务就是提供用户体验。组件的任务介于视图(由模板渲染)和应用逻辑(通常包括模型的某些概念)之间。设计良好的组件为数据绑定提供属性和方法,把其它琐事都委托给服务。
各个构造块之间的关系
如下图所示,区域1是Angular提供的模块,可以把它们看做库模块。每个Angular库的名字都带有@angular前缀(例如@angular/core)。我们通过npm包管理工具安装它们,使用时用import导入即可。
区域2可以理解为是一个组件文件(例如app.component.ts文件),模板通过事件绑定使用组件类的方法,模板通过属性绑定来使用组件类中的属性。组件类中可以注入模块中的服务(例如往HeroListComponent组件类注入HeroService服务来获取数据)。
区域3表示Angular的指令是嵌入到模板中使用。区域4表示一个存放各种服务实例的容器–注入器。我们通过依赖注入机制将服务实例注入到组件中。
该文为本人的学习笔记,如有错误,欢迎指正。