如何简单地介绍angular框架(基础概念篇)
为什么写这篇文章:
使用angular开发也有快2年的时间,突然有一天有人问我,你能不能给我简单讲一下angular框架?虽然每天都在使用,却不知如何表述,故决定写这样一篇文章。
一句话知识点:
Angular本身就是用 TypeScript 写成的,核心功能和可选功能作为一组 TypeScript库,根据需要导入到应用中;
Angular 的基本构建块是 NgModule(特性模块),为组件提供编译的上下文环境。Angular应用就是由一组NgModule定义的, NgModule 可以将其组件和一组相关代码(如服务)关联起来,形成功能单元;
组件定义视图,由html模板+css样式+ts组件类构成,也可以根据需要引入服务类。
模板把传统的html和angular模板语法结合起来,是HTML的升级版(插值,属性绑定,双向数据绑定,事件绑定,指令,结构指令,属性指令,管道等等);
css样式可以使用scss和less进行预扩展;
组件类定义应用的数据和业务逻辑;
组件可以把服务类通过DI依赖注入到组件中;
通过Router服务在多个视图之间进行导航功能;
不过路由器会把类似 URL 的路径映射到视图而不是页面。 当用户执行一个动作时(比如点击链接),本应该在浏览器中加载一个新页面,但是路由器拦截了浏览器的这个行为,并显示或隐藏一个视图层次结构;
如果路由器认为当前的应用状态需要某些特定的功能,而定义此功能的模块尚未加载,路由器就会按需惰性加载此模块;
导航路径和你的组件关联起来。 路径(path)使用类似 URL 的语法来和程序数据整合在一起,就像模板语法会把你的视图和程序数据整合起来一样。 然后你就可以用程序逻辑来决定要显示或隐藏哪些视图,以根据你制定的访问规则对用户的输入做出响应;
装饰器类型:
@Component()
@Injectable()
以上是视图的构成:
组件和模板共同构成了视图:
- 组件类上的装饰器@Component()为其添加元数据,包括指向对应模板的指针;
- 模板中的指令和绑定标记会根据程序数据和逻辑修改这些视图;
- DI依赖注入器会为组件提供特定的服务,如路由器服务,提供视图之前的导航,网络服务,与服务器的I/O数据交互。
NgModule简介
它就是一个容器,存放一些内聚的代码块,使用某个特定的功能。它可以包含组件,服务提供商,其他代码文件等。
@NgModule函数的入参:
declarations(可声明对象表) —— 那些属于本 NgModule 的组件、指令、管道。
exports(导出表) —— 那些能在其它模块的组件模板中使用的可声明对象的子集。
imports(导入表) —— 那些导出了本模块中的组件模板所需的类的其它模块。
Providers(服务提供商) —— 本模块向全局服务中贡献的那些服务的创建器。 这些服务能被本应用中的任何部分使用。(你也可以在组件级别指定服务提供商,这通常是首选方式。)
Bootstrap(启动项) —— 应用的主视图,称为根组件。它是应用中所有其它视图的宿主。只有根模块才应该设置这个 bootstrap 属性;
NgModule和组件:
NgModule可以declaration任意数量的其他组件,这些组件可以是通过路由加载,或者是通过模板创建,他们都属于这个NgModule的组件会共享同一个编译上下文环境,即为,imports的模块(服务类,框架的模块)都可以在组件中使用。
组件还可以包含视图层次结构。一个视图层次结构可以混合使用由不同NgModule中的组件定义的视图,比如UI库。宿主视图为起点,下面包含内嵌视图。
Component简介
组件构成视图的一部分。在组件类汇总定义它的应用逻辑,通过定义属性和方法组成的API与视图交互。
组件可以通过TS的构造器参数性属性,通过依赖注入系统将服务提供给该组件。
组件都有生命周期钩子(lifecycle hooks),因为ng会在应用的过程中,对组件进行create、update、destroy操作,故可以根据需要在对应的钩子环节添加额外的应用逻辑。
组件的元数据:
@Component装饰器后面紧跟的类就代表一个组件类(类似于JAVA的注解),并为其制定metadata元数据,即为函数的入参。
元数据告诉angular到哪里获取它的主要构造块,来创建和展示这个组件及其视图。具体来说,它把模板和该组件关联起来,该组件及其模板,共同描述一个视图。
具体配置项如下:
selector:是一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例的视图。
templateUrl:该组件的 HTML 模板文件相对于这个组件文件的地址。 另外,你还可以用 template 属性的值来提供内联的 HTML 模板。 这个模板定义了该组件的宿主视图。
providers:当前组件所需的服务提供商的一个数组。
模板与视图
与组件直接关联的模板就叫宿主视图,该组件还可以定义一个带层次结构的视图,在模板里引入其他组件关联的模板(可以是任何模块中定义的组件),他们就叫内嵌视图。
模板语法
传统HTML与angular模板语法结合,他可以使用数据绑定来协调应用和DOM中的数据,使用Pipes管道对数据进行转换,使用Directives指令在内容上添加逻辑代码。
数据绑定
数据绑定标记有四种形式,每种形式有方向性的,如下图:
双向数据绑定主要用于模板驱动表单,在Form元素中使用,代指起value属性(语法糖:将属性绑定和事件绑定合作成一种独特的写法)。
父子直接通信原理,如下图:
管道
@Pipe()
作用在模板中声明的显示值,做一个对应的转换的一个转换函数。
使用时,使用管道操作符 |
指令
@Directive()
理论上,组件就是一个指令。
Angular的模板是动态的,只有当渲染他们的时候解析,根据指令给出的指示对DOM进行转换。
分类:结构指令 / 属性指令.
结构指令:通过添加,移除,替换DOM元素来修改布局。
属性指令:修改元素的外观或行为。
服务与DI
@Injectable()
组件仅仅提供用于视图模板中数据绑定的属性和方法,让组件更加精简和高效。
可以将诸如从服务器上获取数据,验证用户输入等委托给各种服务,提高模块化和复用性。
依赖注入
组件是服务的消费者。
机制:
注入器injector:在ng启动过程中会自动创建全应用级注入器;
注入器会创建依赖,通过维护一个容器来管理这些依赖,复用;
提供商provider:告诉注入器如何创建或获取依赖,即为新实例。
提供服务
服务可以在自己的metadata元数据中注册为提供商;
为特定的模板或组件注册提供商,在@NgModule()或@Component()元数据中提供;
示例:
方式一:类本身上创建一个单一的共享实例,任何类中都可以直接调用;
这种方式可以让可以让ng通过移除那些未被使用过的服务来优化大小;
方式二:在模块中创建服务商,该服务的作用范围仅在该NgModule中的所有组件可用;
方式三:在组件注册provider,为该组件的每一个新实例提供该服务的一个新实例;
通过 interface 创建模型类,作为某个组件类的属性的类型(TS的类型检查);
基础常识:
- ngModel依赖FormsModule模块;
- 组件必须声明且只能声明(declarations)在一个NgModule中;
- 使用CLI创建组件会自动把它添加到NgModule中,如果手动创建组件,那还需要在对应的模块中声明一次;
- 如果不同模块需要声明同一个组件,可以将这种组件声明到一个公共的模块里面,然后分开在其他需要的模块引入该公共模块,再添加到imports数组中;另外,特别注意,公共慕课必须把组件export出去;
- @Input()装饰器代表的是一个输入属性;
父子通信:
[hero]="selectedHero" 是 Angular 的属性绑定语法;
这是一种单向数据绑定。从 父组件的 selectedHero 属性绑定到目标元素的 hero 属性,并映射到了子组件的 hero 属性;
为什么需要服务
因为组件的职责是聚焦于展示数据,处理与视图的数据交互,而不关心如何获取或保存数据,把这个工作的职责委托给某个服务;
服务可以在多个“相互不知道”的类之间共享信息;
服务提供商provider:某种可以创建或交付一个服务的东西;
组件的构造函数的参数:
- 声明一个私有属性;
- 把它标记为一个服务的注入点;
- HttpClient调用方法时,可以更加明确定义返回的类型;
特别注意:
- 某个服务类里暴露一个属性作为一个数据缓存,那么在组件的模板里使用时,必须在注入该服务时,使用public关键字,因为Angular只会绑定到组件的公共属性;
服务的一个基本流程:
- 创建一个服务类;
- 根注入器中注册为该服务的提供商 / NgModule / Component 注册服务提供商;
- 通过DI机制把它注入到组件中;
- Angular在创建组件时,就会为该组件创建该服务的一个单例实例;
- (服务可以注入其他服务,特别注意各个服务提供商的注册引起的问题;)
Router基础开发
- 引入依赖router库的RouterModule和Routes模块;
- 根模块元数据初始化路由器时,在imports中,使用RouterModule.forRoot(),创建路由所需的服务提供商和指令,然后才开始监听浏览器地址的变化,子路由使用.forChild();
- 在exports中,导出RouterModule的目的,以便于它在整个应用程序中生效;
- 在该模板的根组件的模板中,添加RouterOutlet路由出口;
- 模板中导航:a标签上使用routerLink(它是RouterLink指令的选择器);
{ path: 'detail/:id', component: HeroDetailComponent }
path路径中的冒号 ( : )表示:某个名称是一个占位符,可以在路由参数中获取;
拿路由参数:
- 引入ActivatedRoute模块,它保存着组件实例的路由信息,可以拿到id属性;
- 通过this.route.snapshot.paramMap.get(‘id’)拿到id值,其中,paramMap是一个从URL中提权的路由参数值的字典;
- 反引号(`)定义模板字符串字面量;
- Location模块,与浏览器打交道的,控制浏览器的前进后退操作,例如,this.loaction.back() ;
HTTP基础应用
HttpClient是Angular通过HTTP与远程服务器通讯的重要机制;
在根模块中导入HttpClientModule模块;
HttpClient的Observable总是发出一个值,然后complete,不会再发出其他值;
内部捕获错误,pipe中的catchError拦截失败的observable,可以定义一个方法来返回安全值,让应用继续正常运行;
AsyncPipe管道
<li *ngFor="let hero of heroes$ | async" >
Heroes$中,$是一个约定,表示它是一个Observable;
在模板中可以使用async管道,自动订阅Observable,就不必在组件类中subscribe;
Rxjs的各种操作符(核心库)
通过Observable来编写异步和基于事件的库;
基本概念:
- Observable (可观察对象):表示一个概念,这个概念是一个可调用的未来值或事件的集合;
- Observer (观察者):一个回调函数的集合,它知道如何去监听由 Observable 提供的值;
- Subscription (订阅):表示 Observable 的执行,主要用于取消 Observable 的执行;
- Operators (操作符): 采用函数式编程风格的纯函数 (pure function),使用像 map、filter、concat、flatMap 等这样的操作符来处理集合;
- Subject (主体): 相当于 EventEmitter,并且是将值或事件多路推送给多个 Observer 的唯一方式;
- Schedulers(调度器):用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout 或 requestAnimationFrame 或其他。
学习Rx,主要就是学习各种操作符,这里就不展开介绍。
总结:
以上就是Angular框架的整体介绍,掌握了它的基本运行原理。不仅会用,还能很好的表达出来。