Angular 笔记8(组件与模板——生命周期)

生命周期钩子

生命周期的顺序

  • ngOnChanges():当 Angular 设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象。(与Vue中的computed类似)
  • ngOnInit():在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮 ngOnChanges() 完成之后调用,只调用一次。
  • ngDoCheck():检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。紧跟在每次执行变更检测时的 。(与Vue中的watch类似),ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。
  • ngAfterContentInit():当 Angular 把外部内容投影进组件视图或指令所在的视图之后(类似于Vue中的slot插槽)调用。第一次 ngDoCheck() 之后调用,只调用一次。
  • ngAfterContentChecked():每当 Angular 检查完被投影到组件或指令中的内容之后调用。ngAfterContentInit() 和每次 ngDoCheck() 之后调用
  • ngAfterViewInit():当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。第一次 ngAfterContentChecked() 之后调用,只调用一次。
  • ngAfterViewChecked():每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
  • ngOnDestroy():每当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。在 Angular 销毁指令或组件之前立即调用。

初始化组件或指令

使用 ngOnInit() 方法执行以下初始化任务。

  • 在构造函数外部执行复杂的初始化。组件的构造应该既便宜又安全。比如,你不应该在组件构造函数中获取数据。ngOnInit() 是组件获取初始数据的好地方。
  • 只有在构造完成之后才会设置指令的数据绑定输入属性。如果要根据这些属性对指令进行初始化,请在运行 ngOnInit() 时设置它们。

在实例销毁时进行清理

把清理逻辑放进 ngOnDestroy() 中,这个逻辑就必然会在 Angular 销毁该指令之前运行。

  • 释放资源,这些资源不会自动被垃圾回收。如果你不这样做,就存在内存泄漏的风险。
  • 取消订阅可观察对象和 DOM 事件。
  • 停止 interval 计时器。
  • 反注册该指令在全局或应用服务中注册过的所有回调。
  • 用来通知应用程序的其它部分,该组件即将消失。

一般性例子(见官方文档)

所有生命周期事件的顺序和频率

Peek-A-Boo
顺序:
OnChanges
OnInit
DoCheck
AfterContentInit
AfterContentChecked
AfterViewInit
AfterViewChecked
OnDestroy

频率:

  • 点击update hero按钮时,执行顺序:
    DoCheck
    AfterContentChecked
    AfterViewChecked
    OnChanges: name changed to “Windstorm11!”
    DoCheck
    AfterContentChecked
    AfterViewChecked
  • 点击destroy按钮时,执行顺序:
    DoCheck
    AfterContentChecked
    AfterViewChecked
    OnDestroy
  1. 当数据更新时,OnChanges会触发一次,DoCheck、AfterContentChecked、AfterViewChecked会在OnChanges的前后触发两次;当销毁数据时,OnDestroy会触发一次。
  2. DoCheck、AfterContentChecked 和 AfterViewChecked 钩子。 注意,这三种钩子被触发了很多次,所以让它们的逻辑尽可能保持精简是非常重要的!

使用指令来监视 DOM

<div class="parent">
  <input [(ngModel)]="newName" (keyup.enter)="addHero()">
  <button (click)="addHero()">Add Hero</button>
  <button (click)="reset()">Reset Heroes</button>

  <p></p>
  <div *ngFor="let hero of heroes" mySpy class="heroes">
    {{hero}}
  </div>
  <h4>-- Spy Lifecycle Hook Log --</h4>
  <div *ngFor="let msg of logger.logs">{{msg}}</div>
</div>
@Directive({selector: '[mySpy]'})
export class SpyDirective implements OnInit, OnDestroy {
  constructor(private logger: LoggerService) { }
  ngOnInit()    { this.logIt(`onInit`); }
  ngOnDestroy() { this.logIt(`onDestroy`); }

  private logIt(msg: string) {
    this.logger.log(`Spy #${nextId++} ${msg}`);
  }
}

通过mySpy指令,监听DOM元素的初始化和销毁。

使用变更检测钩子

<div class="parent">
  <h2>{{title}}</h2>

  <table>
    <tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
    <tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
  </table>
  <p><button (click)="reset()">Reset Log</button></p>

  <on-changes [hero]="hero" [power]="power"></on-changes>
</div>
export class OnChangesComponent implements OnChanges {
  @Input() hero: Hero;
  @Input() power: string;

  changeLog: string[] = [];

  ngOnChanges(changes: SimpleChanges) {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur  = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }

  reset() { this.changeLog = []; }
}
  • ngOnChanges监听到了@input输入属性的变化,并分别记录了变量变化前和变化后的值。
  • ngOnChanges只能监听到当前变量(如power和hero)的变化,无法监听当前变量的属性的值(hero.name)的变化

响应视图的变更

AfterView 例子展示了 AfterViewInit() 和 AfterViewChecked() 钩子,Angular 会在每次创建了组件的子组件视图后调用它们。

template: `
  <div>-- child view begins --</div>
    <app-child-view></app-child-view>
  <div>-- child view ends --</div>`
@Component({
  selector: 'app-child-view',
  template: '<input [(ngModel)]="hero">'
})
export class ChildViewComponent {
  hero = 'Magneta';
}
export class AfterViewComponent implements  AfterViewChecked, AfterViewInit {
  private prevHero = '';

  // Query for a VIEW child of type `ChildViewComponent`
  @ViewChild(ChildViewComponent) viewChild: ChildViewComponent;

  ngAfterViewInit() {
    // viewChild is set after the view has been initialized
    this.logIt('AfterViewInit');
  }

  ngAfterViewChecked() {
    // viewChild is updated after the view has been checked
    if (this.prevHero === this.viewChild.hero) {
      this.logIt('AfterViewChecked (no change)');
    } else {
      this.prevHero = this.viewChild.hero;
      this.logIt('AfterViewChecked');
    }
  }
}

下列钩子基于子视图中的每一次数据变更采取行动,它只能通过带@ViewChild装饰器的属性来访问子视图。

  • ngAfterViewInit在子视图初始化时触发
  • ngAfterViewChecked在子视图变更时触发

响应被投影内容的变更

类似于Vue中的slot插槽
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
<ng-content> 标签是外来内容的占位符。 它告诉 Angular 在哪里插入这些外来内容。 在这里,被投影进去的内容就是来自父组件的 <app-child> 标签。

export class AfterContentComponent implements AfterContentChecked, AfterContentInit {
  private prevHero = '';
  comment = '';

  // Query for a CONTENT child of type `ChildComponent`
  @ContentChild(ChildComponent) contentChild: ChildComponent;

  ngAfterContentInit() {
    // contentChild is set after the content has been initialized
    this.logIt('AfterContentInit');
    this.doSomething();
  }

  ngAfterContentChecked() {
    // contentChild is updated after the content has been checked
    if (this.prevHero === this.contentChild.hero) {
      this.logIt('AfterContentChecked (no change)');
    } else {
      this.prevHero = this.contentChild.hero;
      this.logIt('AfterContentChecked');
      this.doSomething();
    }
  }
  // ...
}
  • 只能通过带有@ContentChild装饰器的属性来查询到“子级内容”。
  • AfterContent 在AfterView 之前触发

自定义变更检测逻辑

使用 ngDoCheck() 钩子来检测和处理 Angular 自己没有捕捉到的变化。这个钩子触发的很频繁,类似于Vue中的watch。

ngDoCheck() {

  if (this.hero.name !== this.oldHeroName) {
    this.changeDetected = true;
    this.changeLog.push(`DoCheck: Hero name changed to "${this.hero.name}" from "${this.oldHeroName}"`);
    this.oldHeroName = this.hero.name;
  }

  if (this.power !== this.oldPower) {
    this.changeDetected = true;
    this.changeLog.push(`DoCheck: Power changed to "${this.power}" from "${this.oldPower}"`);
    this.oldPower = this.power;
  }

  if (this.changeDetected) {
      this.noChangeCount = 0;
  } else {
      // log that hook was called when there was no relevant change.
      let count = this.noChangeCount += 1;
      let noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
      if (count === 1) {
        // add new "no change" message
        this.changeLog.push(noChangeMsg);
      } else {
        // update last "no change" message
        this.changeLog[this.changeLog.length - 1] = noChangeMsg;
      }
  }

  this.changeDetected = false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值