6、Angular 2组件的生命周期钩子

如果打算自己写组件库的话,组件的声明周期需要深入掌握。寻常的业务代码倒不会太涉及这方面的东西。

photo1

这里写图片描述

组件生命周期的这些钩子的执行顺序
如下图所示:
image

实现的ngOnChanges(){}钩子方法会在组件的最先运行,比ngOnInit()都更前。

这和angular组件本身的实例化是有关系的。angular在实例化组件时是会先去处理那些输入属性(也就是@Input属性)的。所以会先调用ngOnChanges()。

一些代码我们既能放在constructor方法中,也能放在ngOnInit方法中。但是要注意在constructor方法中最好只做一些简单的赋值,不要做重复的逻辑。获取服务端数据、业务逻辑等不要实现在构造函数中。

ngOnInit

使用angular/cli的组件生成命令时,会自动在组件代码中生成ngOnInit的钩子,表明这个肯定时用得比较多的:

// 这个方法是在OnInit这个接口类中声明的,所以组件类都需要implements这个接口类
// 另外如果要实现ngOnChanges生命周期钩子时,也需要implements对应的OnChanges然后实现对应的方法,这些接口类都是都是在angular/core中的  
// 所以需要在组件类的最前方导入:import { Component, OnInit, OnChanges } from '@angular/core';

// 当然,不写import也不写implements也是可以的,只要实现了对应的方法,angular都是认的

ngOnInit() {

}

ngOnChanges

  这个钩子只有在@Input属性发生变化时才会被调用(而且要求属性是不可变数据类型的时候才会被调用,否则也不会被调用的。)
  string就是一个典型的不可变数据类型,当一个string类型的属性变化之后,它不是直接改属性的值的,而是会新创建一个string然后重新赋给属性,而之前的那个string可能会被垃圾回收掉可能会游离状态或者怎么样我们不清楚。
  而JavaScript对象就不是这样的不可变数据类型,如果你修改了JavaScript对象的某个属性值,它是不会重新建立一个JavaScript对象进行重新赋值的。所以当@Input属性是一个JavaScript对象,而且这个对象的一个属性发生变化时是不会调用ngOnChanges的,只会调用ngDoCheck。

非@Input属性改变不会调用ngOnChanges

ngDoCheck

  • 每发生一次变更就会被调用一次
  • 谁在负责触发变更呢?(Zones,它拦截了所有回调:定时器、事件、Ajax)
  • 不要在ngDoCheck里面做非常消耗性能的事情,会卡死的。

ChangeDetectionStrategy–变更检测策略

分两种:
- ChangeDetectionStrategy.OnPush
- ChangeDetectionStrategy.Default

变更检测的两种策略
- Default:默认是无论哪个组件发生了变化,从根组件开始全局遍历调用ngDoCheck()。
- OnPush:这种策略情况下,只有当组件的@Input属性发生变化的时候才调用本组件的ngDoCheck()。

修改组件脏检查策略的方法:在组件类代码的@Component装饰器中添加changeDetection配置项目(注意在import中也要导入ChangeDetectionStrategy):

@Component({
    selector: '',
    templateUrl: '',
    styleUrls: ['',''],
    changeDetection:ChangeDetectionStrategy.OnPush
})

ngContent – 内容投影

在子组件中使用<ng-content></ng-content>占坑,然后在子组件使用时,内部再加一部分html代码,这样运行之后,写在子组件内部的html代码内容就会显示在ng-content标签的位置:

<!--父组件中使用子组件时,往内部再加html代码-->
...
<child>
    <p>这些东西将会替代子组件html模板中的ng-content标签</p>
</child>
...


<!--然后子组件的html代码中使用ng-content标签就可以了-->
...
<ng-content></ng-content>
...

跟这个东西相关就产生了两个钩子:

ngAfterContentInit && ngAfterContentChecked

父子组件都实现这两个钩子的情况下,会先调用父层的两个钩子,然后再调用子层的两个钩子,顺序如下所示:
1. 父层>ngAfterContentInit
2. 父层>ngAfterContentChecked(会重复调用)
3. 子层>ngAfterContentInit
4. 子层>ngAfterContentChecked(会重复调用)

后面的关于view的是相反的执行顺序。

**投影内容装配完成的时候整个模板还没有装配完
所以,在这两个钩子里面可以修改被绑定的属性**

ngAfterViewinit && ngAfterViewChecked

当父子组件都实现这两个钩子的情况下,会先调用子层再调用父层,与content是相反的,执行顺序如下所示:
1. 子层>ngAfterViewInit
2. 子层>ngAfterViewChecked(会重复调用)
3. 父层>ngAgterViewInit
4. 父层>ngAfterViewChecked(会重复调用)

在组件视图装配的时候调用这两个钩子
视图的装配过程是从子组件向父组件一次进行的。
在这两个钩子里面不能再修改组件上被绑定的属性,否则Angular会抛异常。
ngAfterViewChecked可能会被调用非常多次,如果没有使用OnPush策略,所有实现了这个钩子的组件都会被调用,千万不要在这两个钩子里面做很复杂的事情,会被卡死的

@ViewChild

后面讲一讲,怎样在父组件的ts代码中获取子组件的组件类对象,就是通过@ViewChild(“child1”)这样的方式:

父组件中的html文件使用子组件的方法,使用#进行模板局部变量声明:

...
<child1 #child1></child1>
<child2 #child2></child2>
...

然后再父组件类文件ts代码中使用@ViewCheck()装饰器通过获取局部变量名称来获取对应的子组件类实例:

export class ParentComponent implements OnInit {
    // 使用@ViewChild("组件局部变量名称")装饰器来获取子组件类的实例
    @ViewChild("child1")
    child1:Child1Component

    @ViewChild("child2")
    child2:Child2Component

    ...

    ngOnInit() {
        //然后在代码中可以直接通过子组件类实例调用子组件的方法
        this.child1.aFunction();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值