这个架构图展现了 Angular 应用中的 8 个主要构造块:
模块 (module) 组件 (component) 模板 (template) 元数据 (metadata) 数据绑定 (data binding) 指令 (directive) 管道 服务 (service)和依赖注入 (dependency injection)图中的模板 (Templates)是由 Angular 扩展的 HTML 语法组成,组件 (Components)类用来管理这些模板,应用逻辑部分通过服务 (Services)来完成,然后在模块中打包服务与组件,最后通过引导根模块来启动应用。
模块 (module)
模块由一块代码组成,可用于执行一个简单的任务。
Angular 应用是由模块化的,它有自己的模块系统:NgModules。
每个 Angular 应该至少要有一个模块(根模块),一般可以命名为:AppModule。
Angular 模块是一个带有 @NgModule 装饰器的类,它接收一个用来描述模块属性的元数据对象。
@NgModule几个重要的属性如下:
imports:声明本模块引入的其它模块。
providers:声明本模块引入的服务类。如果在根模块中声明,则它们在应用中的任何部分都可被访问到。
declarations:声明本模块中拥有的视图类。Angular 有三种视图类: 组件、指令和管道 。
exports:声明( declaration )的子集,可用于其它模块中的组件模板 。
bootstrap 指定应用的主视图(称为根组件),它是所有其它视图的宿主。只有根模块才能设置 bootstrap 属性。
// 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 { }
一个最简单的根模块:
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 { }
接下来我们通过引导根模块来启动应用,开发过程通常在 main.ts 文件中来引导 AppModule ,代码如下:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' ; import { AppModule } from './app.module' ; platformBrowserDynamic().bootstrapModule(AppModule);
组件 (component)
组件是一个模板的控制类用于处理应用和逻辑页面的视图部分。
组件是构成 Angular 应用的基础和核心,可用于整个应用程序中。
组件知道如何渲染自己及配置依赖注入。
组件通过一些由属性和方法组成的 API 与视图交互。
创建 Angular 组件的方法有三步:
从 @angular/core 中引入 Component 修饰器 建立一个普通的类,并用 @Component 修饰它 在 @Component 中,设置 selector 自定义标签 ,以及 template 模板
模板 (template)
Angular模板的默认语言就是HTML。
我们可以通过使用模板来定义组件的视图来告诉 Angular 如何显示组件。以下是一个简单是实例:
< div > 网站地址 : {{site}} </ div >
在Angular中,默认使用的是双大括号作为插值语法,大括号中间的值通常是一个组件属性的变量名。
元数据 (metadata)
元数据告诉 Angular 如何处理一个类。
考虑以下情况我们有一个组件叫作 Component ,它是一个类,直到我们告诉 Angular 这是一个组件为止。
你可以把元数据附加到这个类上来告诉 Angular Component 是一个组件。
在 TypeScript 中,我们用 装饰器 (decorator) 来附加元数据。
实例
@Component({ selector : 'mylist', template : ' < h2 > 菜鸟教程 </ h2 > ' directives : [ComponentDetails] }) export class ListComponent{...}
@Component 装饰器能接受一个配置对象,并把紧随其后的类标记成了组件类。
Angular 会基于这些信息创建和展示组件及其视图。
@Component 中的配置项说明:
selector - 一个 css 选择器,它告诉 Angular 在 父级 HTML 中寻找一个 <mylist> 标签,然后创建该组件,并插入此标签中。
templateUrl - 组件 HTML 模板的地址。或者直接写html内容,用“`”括起来
directives - 一个数组,包含此模板需要依赖的组件或指令。
providers - 一个数组,包含组件所依赖的服务所需要的依赖注入提供者。
数据绑定 (data binding)
数据绑定为应用程序提供了一种简单而一致的方法来显示数据以及数据交互,它是管理应用程序里面数值的一种机制。
通过这种机制,可以从HTML里面取值和赋值,使得数据的读写,数据的持久化操作变得更加简单快捷。
如图所示,数据绑定的语法有四种形式。每种形式都有一个方向——从 DOM 来、到 DOM 去、双向,就像图中的箭头所示意的。
代码例子:
//插值表达式 在 HTML 标签中显示组件值。 < li > {{hero.name}} </ li > //属性绑定 把父组件 HeroListComponent 的 selectedHero 的值传到子组件 HeroDetailComponent 的 hero 属性中。 < hero-detail [hero] ="selectedHero" ></ hero-detail > //事件绑定 用户点击英雄的名字时调用组件的selectHero方法。 < li (click) ="selectHero(hero)" ></ li > //双向绑定 数据属性值通过属性绑定从组件流到输入框。用户的修改通过事件绑定流回组件,把属性值设置为最新的值。 < input [(ngModel)] ="hero.name" >
可能大家对各种括号看的眼花了, 总结一下:
双花括号是单向绑定,传递的是值。方向是 组件 -> 模板 。 方括号是单向绑定,传递的是属性。方向是 组件 -> 模板 。 圆括号是事件绑定,处理 点击等活动(action) 。方向是 模板 -> 组件 。 方括号套圆括号是双向绑定,方向是 组件 <-> 模板 。
指令 (directive)
Angular模板是动态的 。当 Angular 渲染它们时,它会根据指令对 DOM 进行修改。
指令是一个带有"指令元数据"的类。在 TypeScript 中,要通过 @Directive 装饰器把元数据附加到类上。
在Angular中包含以下三种类型的指令:
属性指令:以元素的属性形式来使用的指令。 结构指令:用来改变DOM树的结构 组件:作为指令的一个重要子类,组件本质上可以看作是一个带有模板的指令。
< li *ngFor ="let site of sites" ></ li > < site-detail *ngIf ="selectedSite" ></ site-detail >
*ngFor 告诉 Angular 为 sites 列表中的每个项生成一个 <li> 标签。
*ngIf 表示只有在选择的项存在时,才会包含 SiteDetail 组件。
管道
Angular 的管道可以让你在模板中声明显示值的转换逻辑。 带有 @ Pipe 装饰器的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。
Angular 自带了很多管道,比如 date 管道和 currency 管道,完整的列表参见 Pipes API 列表 。你也可以自己定义一些新管道。
要在 HTML 模板中指定值的转换方式,请使用 管道操作符 (|) 。
{{interpolated_value | pipe_name}}
你可以把管道串联起来,把一个管道函数的输出送给另一个管道函数进行转换。 管道还能接收一些参数,来控制它该如何进行转换。比如,你可以把要使用的日期格式传给 date 管道:
<!-- Default format: output 'Jun 15, 2015' --> < p > Today is {{today | date}} </ p > <!-- fullDate format: output 'Monday, June 15, 2015' --> < p > The date is {{today | date:'fullDate'}} </ p > <!-- shortTime format: output '9:43 AM' --> < p > The time is {{today | date:'shortTime'}} </ p >
服务 (service)
服务是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。
Angular 把组件和服务区分开,以提高模块性和复用性。
通过把组件中和视图有关的功能与其他类型的处理分离开,你可以让组件类更加精简、高效。 理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些模型的概念)的中介者。
组件不应该定义任何诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作。 而要把这些任务委托给各种服务。通过把各种处理任务定义到可注入的服务类中,你可以让它可以被任何组件使用。 通过在不同的环境中注入同一种服务的不同提供商,你还可以让你的应用更具适应性。
Angular 不会强制遵循这些原则。它只会通过依赖注入让你能更容易地将应用逻辑分解为服务,并让这些服务可用于各个组件中。
依赖注入 (dependency injection)
依赖注入是提供类的新实例的一种方式,还负责处理类所需的全部依赖。 大多数依赖都是服务 。 Angular 使用依赖注入来提供新组件以及组件所需的服务。
比如我们要给某组件导入 HeroService 这个服务,看这段代码:
constructor(private service: HeroService) { ... }
当 Angular 创建组件时,会首先为组件所需的服务找一个注入器( Injector ) 。
注入器是一个维护服务实例的容器,存放着以前创建的实例。
如果容器中还没有所请求的服务实例,注入器就会创建一个服务实例,并且添加到容器中,然后把这个服务返回给 Angular 。
当所有的服务都被解析完并返回时, Angular 会以这些服务为参数去调用组件的构造函数。 这就是依赖注入 。
我们必须先用注入器 injector 为 HeroService 注册一个提供商 provider。意思就是我们必须在 providers 写上才能用,看这段代码:
@Component({ selector: 'hero-list', templateUrl: './hero-list.component.html', providers: [ HeroService ] })
参考:
官方文档
Angular 4.0 架构详解
Angular 2 架构
查看更多关于Angular学习笔记—架构简述的详细内容...