[NGX]Angular组件/指令生命周期简介

Angular中所有的组件和指令都有相同的生命周期钩子,在@angular/core库中定义;可能很多初学者(包括我-。-)都只知道ngOnInit()和ngOnDestroy(),这里简单介绍一下Angular的几个生命周期的顺序和作用。

执行顺序

按照调用顺序,Angular的生命周期钩子有如下几个,

ngOnChanges() -> ngOnInit() -> ngDoCheck() -> ngAfterContentInit() -> ngAfterContentChecked() -> ngAfterViewInit() -> ngAfterViewChecked() -> ngOnDestroy()

作用

ngOnChanges()

这个钩子的第一次调用肯定会在ngOnInit()执行前触发,一般是用来检测组件/指令的输入属性发生的变化用的,一旦该组件的输入属性(@Input)发生变化,ngx就会出发这个组件的ngOnChanges()方法;

用法:

    ngOnChanges(changes: SimpleChanges) {
        console.log(changes);
        // 第一次调用ngOnchanges()时,changes.prop.firstChange === true;
        // 且changes.prop.previousValue === undefined
    }
复制代码

注意:

ngOnChanges()只能检测到输入属性的变化,检测不到内部属性的变化,也就是说这个钩子不像angular.js里的$watch方法,$watch()方法是可以监视内部属性的变化并执行相应的回调函数的,而ngOnChanges()不可以;

即使是针对输入属性,如果输入属性是字符串,数字等不可变类型,ngOnChanges()会在输入属性发生变化时触发;

如果输入属性是数组,对象等引用类型,往数组里push等,直接修改object.key的值是不会触发ngOnChanges()的,Angular只关心属性的引用是否发生变化,而不会关注对象的某个属性是否发生变化。

如果你要在数组push时触发ngOnChanges(),则应该将push改写成this.array = [...this.array, item]

修改对象key改写为this.obj = Object.assign({}, this.obj, {name: 'blabla'})或者this.obj = {...this.obj, name: 'blabla'}

这里主要使用了es7的展开符做了一个简单的示例,除此之外还可以通过immutable.js等不可变数据类型实现。

ngOnInit()

Angular在对一个组件第一次显示数据绑定和设置组件/指令的输入属性之后,初始化指令/组件。如果你用ng g component创建组件的话,@angular/cli创建的组件文件中是会自带ngOnInit()的。

也就是说,ngOnInit()的执行是在组件/指令类的构造函数执行之后才会执行的,它只会执行一次。通常情况下,我们会把一些初始化逻辑放进ngOnInit()里面,如初始界面的数据的获取等。由于已经执行了构造函数,所以此时angular已经完成了输入属性的绑定。因此你可以放心的使用这些属性作为参数发送ajax请求。

ngOnDestroy()

Angular每次销毁组件/指令前会调用这个方法,通常在ngOnDestroy()里我们会对一些可观察对象进行取消订阅,对定时器进行取消等,防止内存泄漏;

有关取消订阅,事实上不是所有的可观察对象都需要你去手动取消订阅,详见https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87

ngDoCheck()

上面我们提到了ngOnChanges()里面无法对数组的push,对象的直接修改属性值等操作感知变化,当时我们采用了Object.assign和es7的展开符解决了这个问题。Angular本身提供了ngDoCheck()这个生命周期钩子来侦测这些他无法检测到的变更。

这个钩子类似angular.js中的$watch()方法,只要这个组件的属性发生变化,就会触发这个生命周期钩子。因此它的触发会很频繁,所以不要在里面写很复杂的逻辑,这样会导致程序的开销异常的大。

ngAfterContentInit()和ngAfterContentChecked()

这两个afterContent钩子都是在Angular进行内容投影时触发的。

ngAfterContentInit()第一次执行ngDoCheck()后执行,且只会执行一次。

内容投影就是将其他组件的html内容插入到本组件指定位置的方法。通常组件中会有一个<ng-content></ng-content>元素作为placeholder。

当组件的标签中有其他html标签时,说明存在内容投影。

ngAfterContentChecked()会在每次完成被投影组件的变更检测后执行,第一次执行是在第一次调用ngAfterContentInit()后执行。

这两个钩子发生在组件视图组合完成之前,所以此时仍然是可以更新组件属性而不用担心触发ExpressionChangedAfterItHasBeenCheckedError的。

ngAfterViewInit()和ngAfterViewChecked()

Angular会在每次创建了组件的子视图后调用他们(也就是组件中的子组件的视图);

要想访问子视图,需要通过@ViewChild()装饰的属性来实现。

// app.component.ts
import { Component, AfterViewInit, AfterViewChecked, ViewChild } from '@angular/core';
import { DetailComponent } from './detail/detail.component';

@Component({
  selector: 'app-root',
  templateUrl: `
  <div>
    <app-detail></app-detail>
  </div>
  `,
  styleUrls: []
})
export class AppComponent implements AfterViewInit, AfterViewChecked {

  @ViewChild(DetailComponent) detailView: DetailComponent;

  ngAfterViewInit() {
    console.log('after view init');

  }

  ngAfterViewChecked() {
    console.log('after view checked, ', this.detailView);
  }
}
复制代码
// detail.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-detail',
  templateUrl: `<input type="text" [(ngModel)]="innerName">`,
  styleUrls: []
})
export class DetailComponent implements OnInit {

  public innerName = 'yyz';

  constructor() { }

  ngOnInit() {}
}
复制代码

以上示例中,每次修改子组件中的input框的值,父组件中的ngAfterViewChecked()都会触发,并可以获取子组件中的属性值。ngAfterViewChecked()也是一个会被频繁调用的生命周期,所以里面的代码逻辑应当尽量精简;

另外,如果在ngAfterViewChecked()中直接修改组件属性,会触发一个错误ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked。有关这个错误的具体原因可以另外在写一篇文章,所里这边不赘述了。因为Angular本身的单项数据流规则禁止在视图被组合好之后再更新视图。afterView的两个钩子都是在视图被组合好之后触发的。要避免这个错误,需要将操作推到浏览器下一个event loop。

ngAfterViewChecked() {
    console.log('after view checked, ', this.detailView);
    // this will throw one error
    // this.title = this.detailView.innerName + 'after';
    if (this.detailName !== this.detailView.innerName + 'after') {
      setTimeout(() => {
        this.detailName = this.detailView.innerName + 'after';
      });
    }
}
复制代码

结束! 哈

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值