轻便快捷的 Angular 4 学习教程(2)

本次课程把我个人对于 Angular 的理解分享给大家,为了提升学习效率,也能让大家能更好的形成一个 Angular 知识架构;本文内容更偏向理论知识,略显枯燥,但是文章精华很多,内容“真的很实在”!(部分内容引用自官方文档)

Angular 架构概览

官方给出的 Angular 全景图

Framework

上边这个架构图展现出了 Angular 应用中的 8 个主要构造块:

  • 1.模块 (module)

  • 2.组件 (component)

  • 3.模板 (template)

  • 4.元数据 (metadata)

  • 5.数据绑定 (data binding)

  • 6.指令 (directive)

  • 7.服务 (service)

  • 8.依赖注入 (dependency injection)


在现代前端开发中,组件的概念已经深入人心了。所谓组件,简单点说就是具有自己的结构样式以及功能的独立模块,我们的页面就是由一个个这样的组件组合而成的。

在 Angular 中,我们可以根据具体需求创建自己的组件。Angular 中的组件由一个类来进行构建,当然这个类需要通过一个元数据来构建它,元数据的作用就是把一个普通的类变成一个组件类。组件的结构由元数据中的 template / templateUrl 来定义。然后通过数据绑定和指令把组件的数据以及逻辑与组件模板进行关联。通过以上这些就能定义一个具有独立结构与功能的组件了。

当然一个完整的应用仅仅只有组件还不行,组件可能会根据不同的业务场景和用户操作产生不一样的结果,这些具体业务可能和组件本身没有太大关系,比如数据的更新与提交,与后端的通信等,那这些和组件无关的内容,我们把他独立出来,作为一种服务进行单独管理。在组件中我们可以通过一种叫做依赖注入的方式调用具体的服务。

最后,我们可以把组件或者服务通过 Angular 提供的模块进行封装管理,对外提供访问的接口,我们也可以通过模块的方式引入第三方的组件和服务,可以很好解决项目中 组件 / 服务 与第三方 组件 / 服务 之间的冲突问题!

下面我们针对这张全景图来逐一理解每块的概念

1、module-模块

首先我们知道:所谓“模块”是组织应用以及使用外部库扩展应用的最佳路径。Angular 应用是模块化的!他有自己的模块系统————NgModules!

很多 Angular 库也都是模块,例如:FormsModule、HttpModule、RouterModule。 很多第三方库也封装成了 Angular 模块例如:Material Design、 Ionic、 AngularFire2。

so : Angular 模块很重要!Angular 模块很重要!Angular 模块真的很重要!

不管你是 Angular 也好或者 ionic 也好,只要新建的项目,每个 Angular 应用至少有一个模块(根模块),也就是说都要存在一个根模块(命名一般为 AppModule)。

如果我们使用了模块化来创建应用,包括 AppModule 根模块,都会有一个 NgModule 装饰器的类。NgModule 接收一个元数据对象,这个对象告诉 Angular 如何编译和运行模块代码。 它标记出该模块拥有的组件、指令和管道, 并把它们的一部分公开出去,以便外部组件使用它们。

另外我们新建的页面,如果不使用懒加载,都要在 NgModule 中先声明后使用。

打开我们 cli 创建的(src/app/app.module.ts)文件:

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 { }(PS:根模块不需要导出任何东西,因为其它组件不需要引导根模块。这里只是作为演示)

这是一个简单的典型的根模块,NgModule 是一个装饰器函数,它是接收一个用来描述模块属性的元数据对象。里边重要的几个属性分别是:

  • declarations:声明本模块中拥有的视图类。Angular 有三种视图类:组件、指令和管道。

  • exports:declarations 的子集,可用于其它模块的组件模板。

  • imports:本模块声明的组件模板需要的类所在的其它模块。

  • providers:服务的创建者,并加入到全局服务列表中,可用于应用任何部分。

  • bootstrap:指定应用的主视图(称为根组件),它是所有其它视图的宿主。只有根模块才能设置 bootstrap 属性。

那么在开发的时候,我们通常会在一个 main.ts 文件中引导 AppModule 根模块:

(打开 src/main.ts 文件可以看到如下)

import { enableProdMode } from '@angular/core';

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

import { environment } from './environments/environment';

if (environment.production) {

enableProdMode();

}

platformBrowserDynamic().bootstrapModule(AppModule);

在图中的意义:看图左上角,模块是用来接收一个用来描述模块属性元数据对象的。

2.component-组件、template-模板 、metadata-元数据

举个官网栗子:

  1. @Component({
  2. selector: 'hero-list',
  3. templateUrl: './hero-list.component.html',
  4. providers: [ HeroService ]
  5. })
  6. export class HeroListComponent implements OnInit {
  7. /* . . . */
  8. }

Component

组件是一个装饰器,他能接受一个配置对象, Angular 会基于这些信息创建和展示组件及其视图。

  • selector:CSS 选择器,它告诉 Angular 在父级 HTML 中查找 标签,创建并插入该组件。

  • templateUrl:组件 HTML 模板的模块相对地址,如果使用 template 来写的话是用“`”这个符号来直接编写 HTML 代码。

  • providers:组件所需服务的依赖注入。

template

模板就是那段 HTML 代码,可以用 templateUrl 引入外面的,也可以用 template`` 直接写。

metadata

元数据装饰器用类似的方式来指导 Angular 的行为。 例如 InputOutputInjectable 等是一些最常用的装饰器,用法就不在这里展开了。

在图中的意义:看图中间那一块,模板、元数据和组件共同描绘出这个视图。

3.data binding-数据绑定

Angular 支持数据绑定,一种让模板的各部分与组件的各部分相互合作的机制。 我们往模板 HTML 中添加绑定标记,来告诉 Angular 如何把二者联系起来。数据绑定的语法有四种形式。每种形式都有一个方向 —— 绑定到 DOM 、绑定自 DOM 以及双向绑定

看一下示例代码:

  1. <li>{{hero.name}}</li>
  2. <app-hero-detail [hero]="selectedHero"></app-hero-detail>
  3. <li (click)="selectHero(hero)"></li>

a.插值表达式

  • 显示组件的 hero.name 属性的值

    • <li>{{hero.name}}</li>

b.属性绑定

  • 把父组件selectedHero的值传到子组件的hero属性中

    • <hero-detail [hero]="selectedHero"></hero-detail>

c.事件绑定

  • 用户点击英雄的名字时调用组件的 selectHero 方法

    • <li (click)="selectHero(hero)"></li>

d.双向绑定

  • 数据属性值通过属性绑定从组件流到输入框。用户的修改通过事件绑定流回组件,把属性值设置为最新的值

    • <input [(ngModel)]="hero.name">

可能大家对各种括号看的眼花了,总结一下:

看!图!

databinding

  • 双花括号是单向绑定,传递的是值。方向是 组件 -> 模板。
  • 方括号是单向绑定,传递的是属性。方向是 父组件 -> 子组件。
  • 圆括号是事件绑定,处理点击等活动(action)。
  • 方括号套圆括号是双向绑定,方向是 组件 <-> 模板。

在图中的意义:看全景图中间那一块,数据绑定给模板和组件提供数据交互的方式。

4.directive-指令

我们知道 Angular 模板是动态的。当 Angular 渲染这些模板的时候时,它会根据指令提供的操作对 DOM 进行转换。

组件是一个带模板的指令; Component 装饰器实际上就是一个 Directive 装饰器,只是扩展了一些面向模板的特性。

官方解释:While a component is technically a directive, components are so distinctive and central to Angular applications that this architectural overview separates components from directives.
Two other kinds of directives exist: structural and attribute directives.

严格来说组件就是一个指令,但是组件非常独特,并在 Angular 中位于中心地位,所以在架构概览中,我们把组件从指令中独立了出来。

我们要提到的指令是以下两种类型:

  • 结构型 structural 指令和属性 attribute 型指令。

  • 结构型指令是 ngFor、ngIf 这种的,通过在 DOM 中添加、移除和替换元素来修改布局。

栗子:

  1. <li *ngFor="let hero of heroes"></li>
  2. <app-hero-detail *ngIf="selectedHero"></app-hero-detail>
  • 属性型指令是 ngModel 这种的,用来修改一个现有元素的外观或行为。

栗子:

  1. <input [(ngModel)]="hero.name">

Angular 还有少量指令,它们或者修改结构布局(例如 ngSwitch ), 或者修改 DOM 元素和组件的各个方面(例如 ngStyle 和 ngClass)。

当然,我们也能编写自己的指令。像 HeroListComponent 这样的组件就是一种自定义指令(后续文章会详细写到)

在图中的意义:看全景图右上角那一块,组件是一个带模板的指令,只是扩展了一些面向模板的特性。

5.service-服务

官方文档的概念:

服务是一个广义范畴,包括:值、函数,或应用所需的特性。服务没有什么特别属于 Angular 的特性。Angular 对于服务也没有什么定义,它甚至都没有定义服务的基类,也没有地方注册一个服务。

“服务”不是具体的什么东西,就是一个概念了解下就行。Angular 中组件是最大的服务消费者。

官方给出的栗子:

HeroService 类,用于获取 hreo 数据,并通过一个已解析的承诺 (Promise) 返回它们。 HeroService 还依赖于 Logger 服务和另一个用于处理服务器通讯的 BackendService 服务。

  1. export class HeroService {
  2. private heroes: Hero[] = [];
  3. constructor(
  4. private backend: BackendService,
  5. private logger: Logger) { }
  6. getHeroes() {
  7. this.backend.getAll(Hero).then( (heroes: Hero[]) => {
  8. this.logger.log(`Fetched ${heroes.length} heroes.`);
  9. this.heroes.push(...heroes); // fill cache
  10. });
  11. return this.heroes;
  12. }
  13. }

组件类应保持精简。组件本身不从服务器获得数据、不进行验证输入,也不直接往控制台写日志。 它们把这些任务委托给服务。

在图中的意义:看图左下角角那一块,服务是用来封装可重用的业务逻辑。

6.dependency injection-依赖注入

依赖注入是提供类的新实例的一种方式,还负责处理类所需的全部依赖。大多数依赖都是服务。 Angular 使用依赖注入来提供新组件以及组件所需的服务。

比如我们要给某组件导入 HomeService 这个服务,看这段代码:

  1. constructor(private service: HeroService) {
  2. ...
  3. }

这个 constructor 就是构造函数,依赖注入在 constructor 中进行。你也许会觉得前面写上 private、public 之类的很怪,这是 TypeScript 语法比较特殊,习惯就好。

当 Angular 创建组件时,会首先为组件所需的服务请求一个注入器 injector。

注入器维护了一个服务实例的容器,这个容器存放着以前创建的实例。 如果所请求的服务实例不在这个容器中,注入器就会创建一个服务实例,并且添加到这个容器中,然后把这个服务返回给 Angular。 当所有请求的服务都被解析完并返回时,Angular 会以这些服务为参数去调用组件的构造函数。 这就是依赖注入 。

嘘!别说话!看图

injector-injectors

如果注入器还是没有 HeroService,我们必须先用注入器 injector 为 HeroService 注册一个提供商 provider。

第一种方式可以在 Component 元数据中的 providers 属性中把它注册在组件层:

  1. @Component({
  2. selector: 'hero-list',
  3. templateUrl: './hero-list.component.html',
  4. **providers: [ HeroService ]**
  5. })

把它注册在组件级表示该组件的每一个新实例都会有一个服务的新实例

建议采用下边的这种方式,把提供商添加到根模块上,以便在任何地方都使用服务的同一个实例:

  1. (src/app/app.module.ts 文件)
  2. providers: [
  3. BackendService,
  4. **HeroService,**
  5. Logger
  6. ]
官方对于依赖注入的要点的总结:
  • 1.依赖注入渗透在整个 Angular 框架中,被到处使用。
  • 2.注入器 (injector) 是本机制的核心。
    • a. 注入器负责维护一个容器,用于存放它创建过的服务实例。
    • b.注入器能使用提供商创建一个新的服务实例。
  • 3.提供商是一个用于创建服务的配方。
  • 4.把提供商注册到注入器。

在图中的意义:看图左下角角那一块,依赖注入主要用来导入服务。

总结:

理论的东西总是枯燥乏味的,但是希望读者耐心的去理解,这里一共提到了 Angular 的八个主要的构造块,能把这些基础知识理解并吸收成自己的东西,对于入门 Angular 童鞋来说已是绰绰有余,对于后期开发有很大的帮助,后续还会有部分重要的 Angular 特性和服务会持续更新

  • 原创文章:妙味全职讲师-田亚星、钟毅
  • 欢迎转载,转载请注明出处且加上链接,谢谢!

参考文献:

[Angular 官方文档]


对本篇内容有什么不明白?可以给我们留言,我们会不断改进,让大家看得更明白。祝大家学业顺利~

妙味 Angular 交流微信群:


免费视频获取 或 课程咨询:

QQ:2852509866

微信:miaov-class

电话:010-57269690

妙味网友
请登录后留言,请登录注册
 
 
 
 
 
联系我们

25647892 25647892

25647892

010-57658918

在线微信客服
客服1
客服在线时间

周一至周六上午:9:00~12:00

下午:13:30~18:00

妙味公众号