简述:一个完整的Angular应用主要由六个重要部分构成,分别是:组件,模板,指令,服务,依赖注入,和路由.这些组成部分各司其职,而又紧密协作.
其中,与用户直接打交互的是模板视图,它是构成组件的要素之一.另一要素是组件类,用以维护组件的数据模型及功能逻辑.
路由的功能是控制组件的创建和销毁,从而驱使应用界面跳转切换/
指令与模板相互关联,最重要的作用是增强模板特性,间接扩展了模板的语法.
服务是封装了若干功能逻辑的单元,这个功能逻辑可以通过依赖注入机制引入到组件内部,作为组件功能的扩展.
在Angular应用接受用户指令,加工处理后输出相应视图过程中,组件始终处于这个交互的出入口,这是Angular基于组件设计的体现.
1. 组件:组件在Angular框架中处于最核心的位置,Angular框架基于组件设计,其应用由一系列大大小小的松耦合的组件构成.Angular的模板里可以直接引用组件的成员属性,
组件类和模板成员之间的数据交互称为数据绑定(属性绑定和事件绑定也属于数据绑定的范畴).属性绑定和事件绑定即可用于父子组件的数据传递,也可用于组件数据模型和
视图之间的数据传递.所以在父子组件通信过程中,模板从党类似于桥梁的角色.
Angular是一个响应式系统,每次数据变动几乎都能实现实时处理,并更新对应视图.Angular是以适当的时机去检验对象的值是否被改动,这个适当时机并不是以固定的频率
去执行,而通常是在用户操作事件(如单机),setTimeout或XHR回调等这些异步事件触发之后.Angular捕获这些异步事件的工作是通过Zons库来实现的.
每个组件背后都维护着一个独立的变化监测器,这个变化监测器记录着所属组件的数据变更状态.由于应用是以组件树的形式组织,因此每个应用也有着对应的一棵变化监测树.
当Zones捕获到某异步事件后,它都会通知Angular执行变化监测操作,每次变化监测操作都始与根组件,并以深度优先的原则向叶子组件遍历执行.
变化监测机制使得开发者不必关心数据和变动,结合数据绑定实现模板视图实时更新,这就是Angular强大的数据变化监测机制.变化监测机制提供数据自动更新功能,若需要手动
捕获变化事件做一些额外处理,Angular还提供了完善的生命周期钩子给开发者调用,如ngOnChanges可以满足刚提到的捕获变化事件要求,如ngOnDestroy可以在组件销毁前做一些
清理工作.
2. 模板:Angular模板基于Html,普通的Html亦可作为模板输入.Angular为模板定制出一套强大的语法体系,数据绑定是模板的基本功能,插值也是很常见的数据绑定的语法.插值语法是由
一对双大括号{{}}组成,插值的变量上下文是组件类本身,
如:export class contactComponet{ @Input() item:ContractMOdel;... }
插值是一种单向的数据流动--从数据模型到模板视图,前面提到的三种数据绑定(即属性绑定,事件绑定以及插值)语法的数据流动都是单向的,在某些场景下需要双向的数据流动支持(如表单).
结合属性绑定和事件绑定,Angular模板可实现双向绑定的功能.
如:<input [(ngModel)]="contact.name" />
[()]是实现双向绑定的语法糖,ngModel是辅助实现双向绑定的内置指令.
管道:数据绑定负责数据的传递与展示,而针对数据的格式化显示,Angular提供了管道功能,使用竖线|来表示,
如:<span>{{ contact.telephone | phone }}</span>
angular模板还有很多强大的语法特性,包括上述提到的组件所封装的自定义标签<contact></contact>.
3. 指令:指令与模板的关系密切,指令可以与DOM进行灵活交互,它或是改变样式,或是改变布局.指令的范畴很广,实际上组件也是指令的一种.组件与一般的指令的区别在于:组件带有单独的模板,即
DOM元素,而一般的指令作用在已有的DOM元素上.
一般指令分为两种:
1. 结构指令:结构指令能添加,修改或删除DOM,从而改变布局.
如:<button *ngIf="canEdit">编辑</button>
注意"*"号不能丢,这是语法中的一部分.
2. 属性指令:用来改变元素的外观或行为,使用起来跟普通的HTML元素属性基本相似.
如:<span [ngStyle]="setStyles()">{{contact.name}}</span>
4. 服务:其是封装单一功能的单元,类似于工具库,常被引用到组件内部,作为组件的功能扩展.它可以是一个简单的字符串或JSON数据,也可以是一个函数,甚至一个类,几乎所有的对象都可以封装成
服务.
HTTP是Angular里常用的内置服务,它封装了一系列的异步数据请求接口,但与一般的接口不同,HTTP服务对外暴露的是Reactive Programming规范的接口,基于RxJS实现,严格贯彻响应
式编程思想.
5. 依赖注入:通过依赖注入机制,服务等模块可以被引入到任何一个组件中.可以说依赖注入是一种帮助开发者管理模块依赖的设计模式.
@Component装饰器中的providers属性是依赖注入操作的关键,它会为该组件创建一个注入器对象,并新建所需要注入的对象存储到这个注入器里.组件在需要引入该实例对象时,通过
TypeScript的类型匹配既可以从注入器取出相应的实例对象,无需重复显示实例化.
注意:服务的每一次注入(也就是使用providers的声明),该服务都会被创建出新的实例.组件的所有子组件均默认继承福组件的注入器对象,服用该注入器里存储的服务实例.
6. 路由:在Angular中路由的作用是建立URL路径和组件之间的对应关系,根据不同的URL路径匹配出相应的组件并渲染.
如:[
{path:‘‘,component:ContactListComponent},
{path:‘record‘,component:RecordListComponent},
...
]
注意:上面配置的第一项path的值为空,这表示默认路由.
模板如下:
<div>
<header></header>
<router-outlet></router-outlet>
<footer></footer>
</div>
路由指令router-outlet起着类似于"插座"的作用,根据当前的URL路径,匹配插入对应的组件节点,实现了主体内容(页面)的刷新,这就是Angular路由最基本的功能.路由指还支持多重
嵌套,实现了子路由的功能.如:
[
{path:‘‘,component:ContactListComponent},
{path:‘record‘,component:RecordListComponent,children:[
{path:‘‘,component:AllRecordComponent},
{path:‘miss‘,component:MIssRecordComponent}
]}
]
7. 应用模块:Angular引入了模块机制,是对某些特定功能特性的封装,可能包含若干组件,指令,服务等,甚至拥有独立的路由配置.每个Angular应用至少拥有一个模块,一般需要有一个模块
作为应用的入口.这个入口的模块称为根模块,通过引导运行根模块来启动Angular应用.
7.1 在Angular中,除了根模块外其他的模块类型有:
1.特性模块:(Feature Module),封装某个完整功能的模块.
2.共享模块:(Shared Module)封装一些公共构件的模块.
3.核心模块:(Core Module)存放应用级别核心构件的模块.
通过将特性模块导入到根模块里,即可实现对该特性功能的引入,而模块之间的交互,由Angular处理,这中交互关系对于不同的模块构件各不相同:
1.路由:特性模块也可以自带路由配置,当特性模块导入到根模块后,特性模块的路由配置会自动与根模块里的路由配置合并.
2.组件和指令:默认情况下模块内的组件和指令是私有的,也就是说特性模块被导入根模块后,根模块不能使用该特性模块里的组件和指令,除非该特性模块里暴露出了某些组件或者指令
这些暴露出的组件和指令相当于模块的API.
3.服务:服务的处理则有些特殊,通过依赖注入机制,服务同样可以注入到根模块里,但跟组件里的依赖注入的作用域不相同.注入到该组件的服务只能使用在该组件机器子组件上.而注入
到模块里的服务则在整个应用里均能使用,因为所有模块都共享着同一个应用级别的根注入器.
7.2 Angular已经封装了不少常用的特性模块,如:
1.ApplicationModule:封装一些启动相关的工具.
2.CommonModule:封装一些常用的内置指令和内置管道等.
3.BrowserModule:封装在浏览器平台运行时一些工具库,同时将CommonModule和ApplicationModule打包导出,所以通常在使用时引入BrowserModule就可以了.
4.FormsModule和ReactiveFormsModule:封装表单相关的组件指令等.
5.RouterModule:封装路由相关的组件指令等.
6.HttpModule:封装了网络请求相关的服务等.
7.3 Angular通过引导运行根模块来启动引用,引导的方式有两种:动态引导和静态引导.Angular应用运行之前,都需要经过编译器对模块,组件等进行编译,编译完成后才开始启动应用并
渲染截面.
动态引导和静态引导的区别在于编译的时机,动态引导是将所有代码加载到浏览器后,在浏览器进行编译;而静态引导是将编译过程前置到开发时的工程打包阶段,加载浏览器的将是编译
后的代码.
1.动态引导代码:
import { platformBrowserDynamic } from ‘@angular/platform-browser-dynamic‘;
import { AppModule } from ‘./app.Module‘;
platformBrowserDynamic().bootstrapModule(AppModule);
动态引导是从platformBrowserDynamic函数启动的,该函数从@angular/platform-browser-dynamic文件模块中导入,动态引导的模块AppModule需要启动的模块.
2.静态引导代码:
import { platformBrowser } from ‘@angular/platform-browser‘;
import { AppModuleNgFactory } from ‘./app.module.ngFactory‘;
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
静态引导以platformBrowser()函数启动,这个函数是从@angular/platfrom-browser文件模块中导入的,和动态引导不是同一个.静态引导启动的是AppModuleNgFactory模块,
这是AppModule经过编译后处理后生成的模块(app.module文件编译后生成app.module.ngFactory文件),由于整个文件已经被预先编译,所以编译器并不会打包到项目代码,项目
代码包更小,加载更快,省去编译,启动更快.
总结:动态引导开发流程简单明了,适合小型项目或者大型应用的开发阶段使用,而静态引导需要在开发阶段加入预编译流程,稍显复杂但性能提升明显,推荐使用.
8. Angular源码结构:整个Angular框架的源码是基于ES6模块来组织的(这里的模块是语言级别的模块,而Angular中的模块是应用级别的模块).
Angular的主要架构模块介绍:
[email protected]/core:存放核心代码,如变化检测机制,依赖注入机制,渲染等,核心功能的实现,装饰器(@Common,@Directive等)也会存放到这个模块.
[email protected]/common:存放一些常用的内置组件及内置指令等.
[email protected]/forms:存放表单相关的内置组件及内置指令等.
[email protected]/http:存放网络请求相关的服务等.
[email protected]/router:存放路由相关的组件和指令等.
[email protected]/platform-<x>:存放的是引导启动相关的工具.Angular支持在多个平台下运行,不同的平台都有对应的启动工具,这些启动工具会被封装到不同的模块里.