一.组件生命周期
组件函数被调用过程如下,其中constructor不属于生命周期函数,其他都是
1.组件生命周期函数
- 实际上类上
implement onXXX
可以不加(即接口实现是可选的),只要类中有对应的ngXXX()
方法就会识别成对应的钩子函数去执行【推荐添加:由于误删除某个钩子函数会进行提醒;对组件设计到哪些生命周期一目了然】 ngOnChanges(simpleChange:SimpleChange)
在@Input属性发生变化的时候调用,参数SimpleChange类型对象是字典类型,所有的键都是所有输入@Input的属性名,所有的值还是字典对象,其中是当前值、是否第一次被改变、上一个值ngOnInit()
组件初始化完成时调用ngDoCheck()
在组件脏值检测的时候调用ngAfterContentInit()
当组件内容初始化完成的时候调用【组件内容的初始化】ngAfterContentChecked()
对组件里面的内容脏值检测之后进行的调用【组件内容的脏值检测】ngAfterViewInit()
在组件视图初始化完成后的调用(当前组件和子组件渲染完成时调用)【组件视图的初始化】ngAfterViewChecked()
组件视图的脏值检测之后进行的调用【组件视图的脏值检测】ngOnDestory()
组件销毁时调用【通常是父组件通过ngIf方式引用或路由发生变化的时候会销毁组件】。通常需要做一些清理工作,如在当前组件中使用了setTimeout()/setInterval()
类似方法需要在组件销毁的时候清理掉,否则会导致内存溢出
二.组件类中引用模板
1.模板在组件类中的引用
-
引用实例代码
-
html
<div #helloDiv> ... </div>
-
ts类
export class AppComponent { @ViewChild('helloDiv',{static:true}) //如果此模板是在ngIf包含之下的static为false;如果不是在ngIf包含之下的就是静态的即是true helloDivRef: ElementRef; ngAfterViewInit() { // 通过nativeElement属性的innerHTML添加内容 this.helloDivRef.nativeElement.innerHTML = `<span>Hello</span>`; } }
-
-
html标签
#
后面是给模板或者DOM元素起一个引用名字,以便可以在组建类或模板中进行引用 -
ElementRef是DOM元素的一个包装类,因为DOM元素不是Angular中的类,所以需要一个包装类以便在Angular中使用和标识其类型。其中ElementRef中的nativeElement是指向DOM节点的
三.在模板中引用多个元素
1.模板在组件类中的引用
-
通过指明类型引用模板中的组件
-
模板中使用AppTestComponent组件,selector是app-test
<app-test></app-test>
-
组建类
@ViewChild('AppTestComponent') appTest: AppTestComponent;
-
-
如果想引用模板中的Angular组件,@ViewChild中可以使用引用名,也可以使用组件类型
2.引用多个模板元素
-
通过
@ViewChildren
装饰器声明QueryList<ElementRef>
类型的方式引用多个模板元素-
模板内容
<img #img *ngFor="let item of items" [src]="item.imgUrl" [alt]="item.caption" />
-
组建类
@ViewChild('img') imgs: QueryList<ElmentRef>; // 通过nativeElement操作实现高度样式的变更 ngAfterViewInit() { this.imgs.forEach(item => item.nativeElement.style.height='100px');//将高度变成100px }
-
-
可以使用@ViewChildren,在@ViewChildren中使用引用名或者使用Angular组件/指令的类型。声明类型为
QueryList<?>
-
通常我们对
@ViewChild/@ViewChildren
声明的元素都放在ngAfterViewInit生命周期中操作,以免元素还没有加载被调用是空的
3.Render2的使用
-
直接操作DOM在Angular中不推荐,推荐使用Renderer2[在
@angular/core
中导入]export class TestComponent { constructor(private renderer2: Renderer2) {} ngAfterViewInit() { this.imgs.forEach(item=> { this.renderer2.setStyle(item.nativeElement, 'height', '100px'); //等同于this.imgs.forEach(item => item.nativeElement.style.height='100px'); }); } }
-
使用Renderer2更安全,因为直接操作DOM如果是用户添加进来的代码将不会检查的插入,而Renderer2会进行相关的检查
4.@ViewChild总结
- @ViewChild用来在类中引用模板中的视图节点
- 可以使Angular组件,也可以是HTML元素
- 在AfterViewInit中可以安全的使用@ViewChild引用的元素
- 推荐使用Renderer2操作DOM元素
四.组件的双向绑定
1.ngModel
- 是FormsModule中提供的指令[需要引入FormsModule才能使用 ]
- 使用
[(ngModel)]="变量"
形式进行双向绑定 - 其实是一个语法糖,等同于
[ngModel]="username" (ngModelChange)="username=$event"
2.自定义类似ngModel的语法糖
-
定义组件属性_username和对外输出的变化事件
export class TestComponent { private _username = ''; @Output() usernameChange = new EventEmitter<>(); @Input() public get username(): string {//get是属性的访问符 return this._username; } public set username(value:string) {//写属性值 this._username = value; this.usernameChange.emit(value);//在写入的同时把事件发射出去 } } //此时即使我们定义的属性是_username,但是对外暴露的属性名其实仍然是username
-
使用时等同于ngModel的形式【将父组件的username属性双向绑定到子组件上】
<test-component [(username)]="username"></test-component>
五.模块的概念与划分
1.什么是模块
- 模块就是提供相对独立功能的一组代码
- 模块的组成部分可以有:组件、服务、指令、管道等
2.@NgModule注解
- imports:模块依赖的其他模块**(只能导入模块)**
- exports:暴露给其他模块使用的组件、指令或管道(可以是模块、组件、指令等)
- providers:对于模块内部需要使用的服务
- declarations:当前模块中有哪些组件,让模块内部组件互相知道彼此。表示模块拥有的组件、指令或管道。注意每个组件、指令、管道只能在一个模块中声明
- bootstrap: 在根模块才有的,表示入口的组件是哪个
- 注意:如果导入BrowserModule即可以不用导入CommonModule(其中有ngFor、ngIf等等),因为BrowserModule其中Exports包含CommonModule和ApplicationModule,导入BrowserModule相当于也导入了这两个Module
3.模块的”坑“
- 导入其他模块时,需要知道使用该模块的目的
- 如果是组件,那么需要在每一个需要组件的 模块都进行导入
- 如果是服务,那么一般来说在根模块中导入一次即可
- 需要在每个需要的模块中进行导入的
- CommonModule:提供绑定、
*ngIf
和*ngFor
等基础指令,基本上每个模块都需要导入它 - FormsModule、ReactiveFormsModule:表单模块需要在每一个需要的模块导入
- 提供组件、指令或管道的模块
- CommonModule:提供绑定、
- 只在跟模块导入一次的
- HttpClientModule、BrowserAnimationsModule、NoopAnimationModule
- 只提供服务的模块
4.有效的组织模块
- 通常会提供一个共享模块ShareModule,用来exports公用组件一、公用组件二、公用指令一、公用指令二、公用管道一、公用管道二。从而在使用的时候直接imports共享模块即可以使用所有的内容【服务在根模块中声明】
- 通过会提供根路由模块和根模块,在根模块中定义根组件、公共服务一、公共服务二、公共服务三等
- 在每一个路由模块中会定义负责该模块的路由模块和当前模块,在模块中定义使用的组件、指令、管道和服务