AngularJS学习之四(分支之二):架构概述
架构概述
Angular是一个用于在HTML以及JavaScript或类似于编译为JavaScript的TypeScript语言中构建客户端应用程序的框架。
框架由几个库组成,其中一些是核心的,一些是可选的。
你通过编写HTML模板(带有Angular化的标签 ),编写组件类来管理这些模板,在services中添加 应用程序逻辑,并在模块中组装组件和services。
然后,通过引导程序 root module来启动程序。Angular接管,在浏览器中显示 您的应用内容 ,并根据您提供的指示响应用户 的互动。
当然,这里有比已经提到的更多的内容 。您将在随后的页面 中了解那些详细信息。现在,我们先来关注大局。
这张架构图标识了Angular应用程序的八个主要的构建块:
学习这些构建块,你正在正确的方向前进。
模块
Angular应用程序是模块化的,Angular有一个名为 Angular modules 或NgModules的模块化系统。
Angular模块是一个大问题。本页介绍模块;在Angular modules 页会深度地讲解它们。
每个Angular app具有至少一个Angular模块类,theroot module ,传统命名为AppModule 。
虽然root module可以是一个小应用程序的唯一模块,大多数应用程序有更多的功能 模块,每个内聚的代码 块(模块)专注于一个应用领域,一个工作流,或者是密切相关的功能 集。
一个Angular模块,无论是根还是功能 ,都是一个带着@NgModule
装饰器的类。
装饰器是修改 JavaScript类的函数 。Angular有许多装饰器将元数据附加到类上,以便知道这些类的含义和它们应该如何工作。在 Learn more (了解更多)可以了解更多在网络上的装饰器的介绍。
NgModule
是一个装饰器函数 ,它接受一个元数据对象,其属性 描述模块。最重要的属性 是:
declarations
(装饰品)- 属于这个模块的视图类。Angular有三种视图类: 组件 , 指令 和 管道 。
exports
-declarations(声明)的子集,在其他模块的组件模板中应该可见和可用。
imports
-其他模块,需要导出类,被本模块的组件模板声明。
providers
-本模块 services 的提供者提供给全局 services 集合;它们在应用程序的所有部分都可访问。
bootstrap
-主要应用视图,称为根组件,承载所有其他应用视图。只有root module 应设置这个bootstrap属性 。
这里有一个简单的根模块:
app/app.module.ts
COPY CODE
import { NgModule } from '@angular/core' ; BrowserModule '@angular/platform-browser' @NgModule ({ imports : [ ], providers Logger declarations AppComponent exports bootstrap ] }) export class AppModule }
AppComponent(怀疑这里写错了,应该是AppModule
)的export只是说明如何导出;在该示例中实际上不是必需的。一个root module没有理由export任何东西,因为其他组件不需要导入root module。
通过引导它的root module来运行一个应用程序。在开发过程中,你可能会像这样的main.ts 文件 中引导AppModule。
app/main.ts
COPY CODE
platformBrowserDynamic '@angular/platform-browser-dynamic' './app.module' platformBrowserDynamic (). bootstrapModule ( AppModule );
Angular 模块和JavaScript模块
Angular模块-一个装饰着@NgModule的类-是Angular的基本特征。
JavaScript还有自己的模块系统,用于管理JavaScript对象的集合。它完全不同,与Angular模块系统无关。
在JavaScript每个文件 是一个模块,并在该文件 中定义的所有对象属于该模块。该模块声明某些对象是通过export关键词而公共化。其他JavaScript模块使用import语句从其他模块访问公共对象。
可以在网上去学习更多有关JavaScript的模块系统的知识。
这是两个不同和互补的模块系统。同时使用它们来编写您的应用程序。
Angular 库
Angular作为JavaScript模块的一个集合。(这里有些歧义,原本是Angular ships as a collection of JavaScript modules,ships as在这个地方应该如何理解呢?这么翻译,显得和上文不连贯)。你可以把它们理解成为一个库模块。
每一个Angular的库的名字都是以@angular这样的前缀开始的。
您可以用npm包管理器去安装它们,并且用JavaScript的import语句引入它们。
例如,从 @angular/core库中引入AngularComponent 装饰器就像这样:
您还可以使用JavaScript的import语句,从Angular库导入Angularmodules:
;
在上面这个简单的 root module 例子里面,应用程序模块需要在 BrowserModule
里面的内容 。 为了可以访问,像这样把它增加 到 @NgModule 元数据中。
imports 通过这样做,你就可以一起使用Angular和JavaScript的模块系统了。
因为这两个系统 共享了“imports ”和“exports ”这两个词汇,所以很容易把它们搞混。那就先放在那里。随着时间的推移和经验的积累,这个混乱会搞清楚的。
组件
一个控制一片屏幕的组件称为视图。
例如,下面的视图是由组件来控制的。
具有导航链接 的 app root。
英雄列表。
英雄编辑器。
您定义了一个组件的应用程序逻辑 - 它支持 视图-在类内部。这个类和视图通过属性 和方法 的API进行交互。
例如,这个HeroListComponent有一个heroes属性 ,用来返回它从一个服务中获取 的 一个的英雄的数组。HeroListComponent也有一个selectHero()方法 设置selectedHero属性 ,当用户 点击选择从该列表中选择一个英雄。
app/hero-list.component.ts (class)
COPY CODE
HeroListComponentimplements OnInit heroes Hero []; selectedHero constructor ( private service HeroService ) ngOnInit () this . heroes = service getHeroes (); selectHero ( hero selectedHero hero }
Angular在用户 在应用程序中移动时(浏览时)创建,更新和销毁组件。您的app可以就像上面生命的ngOnInit()
那样,通过可选的 lifecycle hooks (生命周期的挂钩) 在每一个时刻进行响应。
模板
您定义一个组件视图和它相伴随的模板。一个模板是一种HTML形式,告诉Angular如何渲染组件。
一个模板看起来像常规HTML,除了一些差异。下面是针对我们的HeroListComponent的 一个模板HeroListComponent:
app/hero-list.component.html
COPY CODE
<h2> Hero List </h2> <p><i> Pick a hero from the list </i></p> <ul> <li * ngFor = "let hero of heroes" ( click ) = "selectHero(hero)" > {{hero.name}} </li> </ul> <hero-detail * ngIf = "selectedHero" [ hero ] = "selectedHero" ></hero-detail>
虽然这个模板使用典型的HTML元素像<h2>
和<p>
,它也有一些不同。类似*ngFor
,{{hero.name}}
,107); padding:0px 4px">(click),107); padding:0px 4px">[hero],和<hero-detail>
的代码 ,使用的是 Angular'stemplate syntax (Angular 模板语法)。
在模板的最后一行时,这个<hero-detail>标签 是表示一个新组件的自定义 元件,HeroDetailComponent。
该HeroDetailComponent是一个不同于你一直在看的HeroListComponent的组件。该HeroDetailComponent(代码 没有显示 )呈现一个特定的英雄的事实,即用户 从所呈现的列表HeroListComponent中选择英雄。该HeroDetailComponent是HeroListComponent的孩子。
请注意如何<hero-detail>轻松地位于原生的HTML元素中。自定义 组件与原生HTML在同一布局中无缝组合。
元数据告诉 Angular 如何去处理一个类。
Looking back at the code (沿着代码 往回看)找到HeroListComponent
,你可以看到它仅仅是一个类。看不到任何框架的证据,里面也根本没有“Angular”。
事实上,HeroListComponent
真的只是一个类。 在你把它告诉Angular之前,它并不是一个组件。
想要告诉AngularHeroListComponent
是一个组件,那就给这个类附属上元数据。
在 TypeScript中,你使用一个decorator(装饰器) 关键字去附属上元数据。这里有一些关于HeroListComponent
的元数据:
app/hero-list.component.ts (Meta data)
COPY CODE
@Component moduleId module id ,100)"> selector 'hero-list' templateUrl 'hero-list.component.html' HeroService /* . . . */ }
这里有一个@Component
装饰器,这会立刻把在它下面的这个类标识成一个组件类。,
这个@Component
装饰器采用了一个配置对象,这个对象具有Angular所需要的去创建和表示组件和它的视图的信息。
这里有一些可能的@Component
配置选项:
moduleId
:为模块相关的URL(类似templateUrl ),设置基础地址的源 (module.id
) 。
providers:为服务提供组件所需要的依赖注入提供者的数组(array ofdependency injection providers) 。这是一个办法,能让Angular告诉这个组件构造器需要一个HeroService
这样它可以获取 英雄的列表去显示 。
在@Component
中的元数据告诉Angular哪里去获得你为组件指定的主要的构件块。
模板、元数据、和组件一起描述了一个视图。
应用其它元数据装饰器也可以类似地指导Angular的行为。@Injectable
、@Input
、和@Output
是一些最流行的装饰器。
这个架构(原文
The architectural takeaway, 暂时不确认如何解释) 就是你必须在你的
代码 中
增加 元数据,这样Angular才知道要做什么。
数据绑定
没有框架,你需要负责将数据值送入HTML控件中,并且把用户 地反应变成行为和数据的更新。 手动编写这样的推/拉逻辑是令人乏味的,容易产生错误 ,是任何有经验的jQuery程序员可以作证的一场噩梦。
Angular支持 数据绑定, 一种机制用于组件的部分和模板对应部分的协作。给模板HTML增加 绑定标签 去告诉Angular如何去连接双方。
就像图例所示,有四种格式的数据绑定语法。每一种格式都有一个方向,面向DOM的,从DOM出来的,或者是双方向的。
HeroListComponentexample 模板有三种格式:
app/hero-list.component.html (binding)
COPY CODE
<li> {{hero.name}} </li> <hero-detail [ hero ] = "selectedHero" ></hero-detail> <li ( click ) = "selectHero(hero)" ></li>
双向数据绑定 是一个重要的第四种格式,使用ngModel
指示符, 在单一的符号中捆绑着属性 和事件绑定。这里有一个从HeroDetailComponent
模板里面找到的例子:
app/hero-detail.component.html (ngModel)
<input [( ngModel )] = "hero.name" >
在双向绑定中,一个数据属性 值使用属性 绑定从组件流向输入框。使用 事件绑定,用户 的变化也反向流回组件,重设属性 的最后的值。
Angular 在一次JavaScript 事件循环中 处理所有 数据绑定,从应用组件树的根直到所有的孩子组件。
数据绑定在一个模板和它的组件之间担任着很重要的交流角色。
指示符
Angular模板是动态的。当Angular渲染它们的时候,它根据指示符的指导来改变DOM。
一个指示符是一个带有一个@Directive
装饰器的类。一个组件是一个directive-with-a-template(指示符和一个模板); @Component
装饰器实际上是一个@Directive
装饰器扩展后,带有面向模板的功能 。
尽管一个组件技术上是一个指示符,但组件是这样的清晰,位于Angular应用程序的中心,这个结构化的总览还是选择将指示符和组件分开。
两个其它 类型的指示符也存在:structural 和attribute 指示符。
它们尝试去在一个元素标签 中显示 (就像attributes所做 ),有时是通过名字,但是更常见的是作为一个分配或者绑定的目标。
Structural 指示符通过增加 、移除和替换在DOM中的元素来改变布局。
这个example template 使用了两个内建的structural指示符:
app/hero-list.component.html (structural)
COPY CODE
*ngFor = "let hero of heroes" ></li> * ngIf ></hero-detail>
*ngFor
告诉Angular为每一个在heroes
列表中的英雄生成 一个 <li>
。
*ngIf 包含了HeroDetail
组件,仅当一个选择的英雄存在的时候。
Attribute 指示符修改 了一个存在的元素的显示 或者表现。在模板中,他们看起来像规则的HTML属性 ,所有有这样的名字。
ngModel指示符,实现了双向数据绑定,是一个attribute指示符的例子。 ngModel
通过设置它的显示 值属性 和对于变化事件的响应,修改 了一个已经存在元素的表现 (典型的例如<input>
) 。
app/hero-detail.component.html (ngModel)
>
Angular还有一些指示符或者改变了布局结构 (例如,ngSwitch ) 或者修改 了DOM元素或组件的一些方面(例如,ngStyle 和ngClass ).
当然,你也可以编写你自己的指示符。类似HeroListComponent
的组件就是一种自定义 指示符。
服务
服务 是一个广阔的分类 ,包含了任意的值,函数 ,或者你的应用需要的功能 。
几乎所有事物都可以是一个服务。一个服务典型上是一个类,带着一个狭窄的定义好的目的。它将做指定的事情并且把它做好。
例子包括 :
日志服务
数据服务
消息总线
税收计算器
应用程序配置
Angular 关于服务没有什么特别的内容 。Angular没有关于服务的定义。没有服务基础类,没有地方去注册 服务。
但是服务是任何Angular程序的基础。组件是服务的大的消费者。
这里有一个关于服务的类的例子,输出 日志到浏览器控制台:
app/logger.service.ts (class)
COPY CODE
log msg any console log ); error error warn warn }
HeroService获取 英雄并且把他们返回到一个解析了的Promise 。这个HeroService
依赖这个Logger
服务,同时另外一个BackendService
处理着服务器通信的工作。
app/hero.service.ts (class)
[] backend BackendService logger Logger getHeroes backend getAll ). then heroes []) => logger ( `Fetched ${heroes.length} heroes.` push (... // fill cache }); return 服务随处都是。
组件类应该精简。它们不从服务器端获取 数据,校验用户 输入,或者输出 日志到控制台。它们把这些工作委托给服务。
一个组件的工作仅仅是提供用户 体验(注:这里的用户 可能指的是程序员)。它作为视图(被模板所渲染)和应用逻辑(经常包含一个model 的符号)的媒介。一个好的组件代表着对于数据绑定的属性 和方法 。它把所有的别的重要的事情委托给服务。(注:原本It delegates everything nontrivial to services.)
Angular并不强制要求实现 这些原则。如果你用3000行写了一个“kitchen sink ”的组件,它也并不会抱怨。
Angular通过很容易地把你地应用逻辑作为因素注入到服务中来帮助你 遵循 这些原则,并且通过dependency injection 来使这些服务对于组件可用。
依赖注入
Dependency injection(依赖注入) 是一种方法 使用它需要的完全的(fully-formed )依赖去支撑一个类的一个新的实例。大多数依赖是服务。Angular使用依赖注入去提供组件所需要的服务给新的组件。
Angular可以通过看到构造器参数的类型可以告诉一个组件需要哪些服务。例如,你的HeroListComponent
需要一个HeroService
:
app/hero-list.component.ts (constructor)
constructor 当Angular创建一个组件时,它首先会为组件需要的服务申请一个injector (注射器)。
一个注射器维持着它之前创建的服务实例的一个容器。如果一个请求的服务的实例不在容器中,注射器在返回服务给Angular之前会创建一个并且把它放入容器。当所有被请求的服务都被解析和返回,Angular会用这些服务作为参数去呼叫组件构造器。这就是依赖注入 。
HeroService注入的过程看起来有一点像这样:
如果这个注射器没有一个HeroService
,它时怎么知道去创建一个呢?
简而言之,你必须在之前就用注射器注册 一个HeroService
提供者(provider)。一个提供者是一个可以创建或者返回一个服务的东西,最典型的就是服务类本身。
你可以在模块中或者在组件中注册 提供者。
通常来说,给root module 增加 提供者,这样服务的同样实例在任何地方都有效。
app/app.module.ts (module providers)
providers 作为一种选择,在@Component 元数据的providers
属性 的组件层面注册 :
app/hero-list.component.ts (component providers)
})
在组件层面注册 意味着你用那个组件的每一个新实例去获取 一个新的服务的实例。
关于依赖注入需要记住的点:
总结
你已经学到了Angular应用的八个主要构件块的基础了。
那是在Angular应用中的任何东西的基础,从这里起步应该是绰绰有余了。(注:原文是and it's more than enough to get going. )但是它不包括 所有你需要知道的。
这里有一个简要的,字母排序的,重要的Angular功能 和服务的列表。它们中的大多数会在本套文档中覆盖(或者即将是)。
Animations (动画):不需要对动画技术或CSS的深度掌握的,使用Angualr动画库开发动画组件行为。
Change detection (变更检测):变更检测文档会覆盖Angular是如何确定一个组件的属性 值发生了改变,什么时候去更新屏幕,并且它是如何使用zones 去插入异步活动和运行它的变更检测策略。
Events (事件):事件文档将会覆盖如何使用组件和服务去触发事件去发布事件或者订阅 事件。(注:原文是The events documentation will cover how to use components and services to raise events with mechanisms for publishing and subscribing to events.)
Forms (表单):使用基于HTML验证和脏检查来支持 复杂数据场景。
HTTP :用HTTP客户端来和一个服务器进行通信去获取 数据,保存数据,调用 服务端行为。
Lifecycle hooks (生命周期钩子):通过实现生命周期钩子接口,在一个组件的生命周期的关键时刻嵌入,从构建到销毁。
Pipes (管道):在模板中使用管道去提升用户 体验,把值转换成一种显示 。考虑这个currency
管道表达式:
price | currency:'USD':true
它把42.33的价格显示 成$42.33
。
Router (路由器):在客户端程序中的页面 间导航,并且绝不会离开浏览器。
Testing (测试):使用Angular Testing Platform 在你的应用部分运行单元测试,就像它们在与Angular结构交互。
相关文章
AngularJS 是一个JavaScript 框架。它可通过 注:建议把脚本放在 元素的底部。这会提高网页加载速度,因...
angluarjs中页面初始化的时候会出现语法{{}}在页面中问题,也即是页面闪烁问题。出现这个的原因是:由于...
AngularJS 通过被称为指令的新属性来扩展 HTML。AngularJS 指令AngularJS 指令是扩展的 HTML 属性,带有...
AngularJS 使用表达式把数据绑定到 HTML。AngularJS 表达式AngularJS 表达式写在双大括号内:{{ expres...
ng-repeat 指令可以完美的显示表格。在表格中显示数据 {{ x.Name }} {{ x.Country }} 使用 CSS 样式为了...
$http是 AngularJS 中的一个核心服务,用于读取远程服务器的数据。读取 JSON 文件下是存储在web服务器上...