Angular 个人深究(四)【生命周期钩子】

定义:

每个组件都有一个被 Angular 管理的生命周期。

Angular 创建它,渲染它,创建并渲染它的子组件,在它被绑定的属性发生变化时检查它,并在它从 DOM 中被移除前销毁它。

Angular 提供了生命周期钩子,把这些关键生命时刻暴露出来,赋予你在它们发生时采取行动的能力。

除了那些组件内容和视图相关的钩子外,指令有相同生命周期钩子。

概览:

 ngOnChanges()

当 Angular(重新)设置数据绑定输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象

当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前。

 ngOnInit()

在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。

在第一轮 ngOnChanges() 完成之后调用,只调用一次。

 ngDoCheck()

检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。

在每个 Angular 变更检测周期中调用,ngOnChanges() 和 ngOnInit()之后。

 ngAfterContentInit()

当把内容投影进组件之后调用。

第一次 ngDoCheck() 之后调用,只调用一次。

 ngAfterContentChecked()

每次完成被投影组件内容的变更检测之后调用。

ngAfterContentInit() 和每次 ngDoCheck() 之后调用

 ngAfterViewInit()

初始化完组件视图及其子视图之后调用。

第一次 ngAfterContentChecked() 之后调用,只调用一次。

 ngAfterViewChecked()

每次做完组件视图和子视图的变更检测之后调用。

ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。

 ngOnDestroy()

当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。

在 Angular 销毁指令/组件之前调用。

范例:

  ngOnChanges()、ngInit

test2.component.ts

// test2.component.ts:

import { Component, OnInit,Input } from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
//需要继承 OnInit、OnChanges 接口
export class Test2Component implements OnInit OnChanges{
// 两个input 来自 父组件 test1
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { }
ngOnInit() {
          //判断 ngOnInit的执行顺序
console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
}

 test1.component.html

<!-- test1.component.html -->
<p>
test1 works!
</p>
<label> test1 value</label>
<input type="text" [(ngModel)]="test1_value1" >
<input type="text" [(ngModel)]="test1_value2" >
<!-- 将test1_value1的值给test2_value1... -->
<app-test2 [test2_value1]="test1_value1" [test2_value2]="test1_value2"></app-test2>

结果:

 

说明:

  • 刚刷新页面时,将test2_value1、test2_value2的值 从 undefined 变成 1、2。
  • 页面更改test1_value1,将test2_value1的值  从  1 变成  11。
  • 以此类推, 可以使用 ngOnChanges对值发生变化时进行处理。
  • ngInit 的执行顺序在 ngOnChanges之后,可以做一下初始化的工作

  ngDoCheck()

test2.component.ts

import { Component, OnInit,Input,OnChanges,DoCheck} from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
//需要实现 DoCheck 接口
export class Test2Component implements OnInit OnChanges DoCheck{
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { } ngOnInit() {
//console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
//console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
//console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
ngDoCheck(){
console.log("执行ngDoCheck");
}
}  

结果:

说明:

  • 刷新页面的时候,执行了两次。
  • 每次鼠标放到,input框上就会执行一次
  • 更改input值,也会执行一次,开销非常大。慎用!

  ngAfterContentInit()

app.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
//templateUrl: './app.component.html',
//在app主组价中,将test2组件放到test1组件中,
template: `<app-test1>
<app-test2></app-test2>
</app-test1>`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
}

test1.component.ts

import { Component, OnInit,OnChanges} from '@angular/core';

@Component({
selector: 'app-test1',
//templateUrl: './test1.component.html',
//ng-content指定的是外来的组件 在组件app中定义的 test2组件
//同样在test1组件中,也增加test2 组件
template: `<div> <ng-content></ng-content> <app-test2></app-test2> </div>`,
styleUrls: ['./test1.component.css']
})
export class Test1Component implements OnInit, OnChanges{
test1_value1:string;
test1_value2:string;
constructor() { } ngOnInit() {
this.test1_value1="1"
this.test1_value2="2"
}
ngOnChanges(){
console.log("onchange");
}
}

test2.component.ts

import { Component, OnInit,Input ,OnChanges,DoCheck,SimpleChanges,AfterContentInit} from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
export class Test2Component implements OnInit,OnChanges,DoCheck,AfterContentInit{
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { } ngOnInit() {
//console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
//console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
//console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
ngDoCheck(){
console.log("执行ngDoCheck");
}
ngAfterContentInit(){
console.log("执行ngAfterContentInit");
}
}

结果:

说明:

  • ngAfterContentInit会在外来内容被投影到组件中之后 调用,也就是说当test2组件以html的形式投影到test1组件之后执行,
  • 因为使用两种方式进行投影了两次,所以ngAfterContentInit执行了两次
  • 其他操作只会增加ngDoCheck的次数,并没有增加ngAfterContentInit的次数

  ngAfterContentCheck()

 test2.component.ts

import { Component, OnInit,Input ,OnChanges,DoCheck,SimpleChanges,AfterContentInit,AfterContentCheck} from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
export class Test2Component implements OnInit,OnChanges,DoCheck,AfterContentInit,AfterContentCheck{
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { } ngOnInit() {
//console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
//console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
//console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
ngDoCheck(){
console.log("执行ngDoCheck");
}
ngAfterContentInit(){
console.log("执行ngAfterContentInit");
}
ngAfterContentChecked(){
console.log("执行ngAfterContentChecked");
}
}

结果:

说明:

  • 在执行ngDoCheck之后 一定会执行一次ngAfterContentInit
  • 每次完成被投影组件内容的变更检测之后调用
  • 其他代码没贴出来,就是跟上一个是一样的

  ngAfterViewInit()

 test1.component.ts

import { Component, OnInit,OnChanges,ViewChild} from '@angular/core';
import {Test2Component} from "../test2/test2.component" @Component({
selector: 'app-test1',
//templateUrl: './test1.component.html',
template: `<div> <input type="text" [(ngModel)]="test1_value1" >
<input type="text" [(ngModel)]="test1_value2" ><ng-content></ng-content>
<app-test2 [test2_value1]="test1_value1" [test2_value2]="test1_value2"></app-test2> </div>`,
styleUrls: ['./test1.component.css']
})
export class Test1Component implements OnInit, OnChanges{
test1_value1:string;
test1_value2:string;
constructor() { }
@ViewChild(Test2Component);
ngOnInit() {
this.test1_value1="1"
this.test1_value2="2"
}
ngOnChanges(){
console.log("onchange");
}
}

test2.component.ts

import { Component, OnInit,Input ,OnChanges,DoCheck,SimpleChanges,AfterContentInit,AfterContentCheck, AfterViewChecked, AfterViewInit} from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
export class Test2Component implements OnInit,OnChanges,DoCheck,AfterContentInit,AfterContentCheck ,AfterViewChecked, AfterViewInit{
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { } ngOnInit() {
//console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
//console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
//console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
ngDoCheck(){
console.log("执行ngDoCheck");
}
ngAfterContentInit(){
console.log("执行ngAfterContentInit");
}
ngAfterContentChecked(){
console.log("执行ngAfterContentChecked");
}
ngAfterViewInit(){
console.log("执行ngAfterViewInit");
}
}

结果:

说明:

  • 在每次创建了组件的子视图后调用,每次在test1组件中创建test2组件时都会调用,
  • 在test1组件中,需要使用@ChildView 装饰器,将test2component装饰一下

  ngAfterViewChecked

 test2.component.ts

import { Component, OnInit,Input ,OnChanges,DoCheck,SimpleChanges,AfterContentInit,AfterContentCheck, AfterViewChecked, AfterViewInit} from '@angular/core';
@Component({
selector: 'app-test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.css']
})
export class Test2Component implements OnInit,OnChanges,DoCheck,AfterContentInit,AfterContentCheck ,AfterViewChecked, AfterViewInit{
@Input() test2_value1: string;
@Input() test2_value2: string;
constructor() { } ngOnInit() {
//console.log("这里执行ngOnInit");
}
ngOnChanges(changes:SimpleChanges){
//console.log(changes);
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
//console.log(`${propName}: 新值 = ${cur}, 旧值 = ${prev}`);
}
}
ngDoCheck(){
console.log("执行ngDoCheck");
}
ngAfterContentInit(){
console.log("执行ngAfterContentInit");
}
ngAfterContentChecked(){
console.log("执行ngAfterContentChecked");
}
ngAfterViewInit(){
console.log("执行ngAfterViewInit");
}
ngAfterViewChecked(){
console.log("执行ngAfterViewChecked");
}
}

结果:

说明:

  • 上图红色框是,页面刷新完后执行的ngAfterViewChecked,前两次是伴随init一起的,后面两个是,test1给test2赋值导致的
  • 上图绿色框是,更改test1中的一个值,导致了更改了test2的值执行的ngAfterViewChecked
  • 我再test1组件的两个input上,鼠标焦点来回切换时,同样也会执行ngAfterViewChecked,还是慎用这个钩子函数吧

  OnDestroy

test1.component.ts

import { Component, OnInit,OnChanges,ViewChild} from '@angular/core';
import {Test2Component} from "../test2/test2.component" @Component({
selector: 'app-test1',
//templateUrl: './test1.component.html',
template: `
<div>
<input type="text" [(ngModel)]="test1_value1" >
<input type="text" [(ngModel)]="test1_value2" >
<ng-content></ng-content>
<app-test2 [test2_value1]="test1_value1" [test2_value2]="test1_value2">
</app-test2>
<div *ngFor="let test of tests" appTest4 class="tests">
{{test}}
</div>
<input type="button"value="add" (click)="addDiv()">
<input type="button"value="delete"(click)="deleteDiv()">
</div>`,
styleUrls: ['./test1.component.css']
})
export class Test1Component implements OnInit, OnChanges{
test1_value1:string;
test1_value2:string;
tests:any;
constructor() { }
@ViewChild(Test2Component) viewChild:Test2Component;
ngOnInit() {
this.test1_value1="1"
this.test1_value2="2"
this.tests=[1,2,3]
}
ngOnChanges(){
console.log("onchange");
}
addDiv(){
this.tests.push("1212");
}
deleteDiv(){
this.tests=[];
}
}

test4.directive.ts

import { Directive, OnInit, OnDestroy  } from '@angular/core';

@Directive({
selector: '[appTest4]'
})
export class Test4Directive implements OnInit, OnDestroy{ constructor() { }
ngOnInit() { console.log("test4 directive ngOnInit")} ngOnDestroy() { console.log("test4 directive ngDestroy");}
}

结果:

说明:

  • 建立一个directive来 监测test1 组件中的 div的生成与销毁
  • 开始有默认的三个值,所有ngOnInit执行了三次
  • 添加一个值,又执行一次ngOnInit
  • 删除所有的值,执行了4次ngDestroy

总结:

  由于生命周期的存在,angular提供了众多的生命周期的钩子,让我们能够很好的在发生变化的时候进行处理。

Angular 个人深究(四)【生命周期钩子】的更多相关文章

  1. Angular 组件通讯、生命周期钩子 小结

    本文为原创,转载请注明出处: cnzt       文章:cnzt-p http://www.cnblogs.com/zt-blog/p/7986858.html http://www.cnblogs ...

  2. angular 生命周期钩子 ngOnInit() 和 ngAfterViewInit() 的区别

    angular 生命周期钩子的详细介绍在 https://angular.cn/guide/lifecycle-hooks  文档中做了介绍. ngOnInit() 在 Angular 第一次显示数据 ...

  3. Angular 5.x 学习笔记(2) - 生命周期钩子 - 暂时搁浅

    Angular 5.x Lifecycle Hooks Learn Note Angular 5.x 生命周期钩子学习笔记 标签(空格分隔): Angular Note on cnblogs.com ...

  4. Angular组件生命周期——生命周期钩子

    生命周期钩子介绍: 1.ngOnChange:响应组件输入值发生变化时触发的事件. 2.ngOnInit:用于数据绑定输入属性之后初始化组件,在第一次ngOnChange之后被调用. a. 组件构造后 ...

  5. Vue学习之生命周期钩子小结(四)

    一.生命周期钩子(函数): 1.每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听.编译模板.将实例挂载到 DOM 并在数据变化时更新 DOM 等.同时在这个过程中也会运 ...

  6. vue 项目实战 (生命周期钩子)

    开篇先来一张图 下图是官方展示的生命周期图 Vue实例的生命周期钩子函数(8个)        1. beforeCreate             刚 new了一个组件,无法访问到数据和真实的do ...

  7. Angular4学习笔记(九)- 生命周期钩子简介

    简介 Angular 指令的生命周期,它是用来记录指令从创建.应用及销毁的过程.Angular 提供了一系列与指令生命周期相关的钩子,便于我们监控指令生命周期的变化,并执行相关的操作.Angular ...

  8. Angular2 -- 生命周期钩子

    组件生命周期钩子 指令和组件的实例有一个生命周期:新建.更新和销毁. 每个接口都有唯一的一个钩子方法,它们的名字是由接口名加上 ng前缀构成的.比如,OnInit接口的钩子方法叫做ngOnInit. ...

  9. Angular25 组件的生命周期钩子

    1 生命周期钩子概述 组件共有9个生命周期钩子 1.1 生命周期的执行顺序 技巧01:测试时父组件传递对子组件的输入属性进行初始化操作 import { Component, Input, Simpl ...

随机推荐

  1. JavaScript学习 - 基础(二) - 基础类型/类型转换

    基础类型 - 数字类型(Number) 1.最基本的数据类型 2.不区分整型数值和浮点型数值 3.所有数字采用64位浮点格式存储,相当于Java和C语言中double格式 4.能表示的最大值 +- 1 ...

  2. Django开发笔记二

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.xadmin添加主题.修改标题页脚和收起左侧菜单 # ...

  3. Flex布局和Bootstrap布局两者的比较

    在Bootstrap中采用12栅格的布局,12份随意分配,但是不能解决5等分,7等分的问题.所以flex布局来协助. bootstrap的布局方式 <div class="row&qu ...

  4. 【转】inotify+rsync实现实时同步

    [转]inotify+rsync实现实时同步 1.1 什么是实时同步:如何实现实时同步 要利用监控服务(inotify),监控同步数据服务器目录中信息的变化 发现目录中数据产生变化,就利用rsync服 ...

  5. js 当前时区

    function formatDateTime(formatDate){ //13位时间戳,java js. (php时间戳为10位) var returnDate; if(formatDate == ...

  6. TCP建立流程 【转】

    解决TCP连接数过多的问题 TCP状态迁移,CLOSE_WAIT & FIN_WAIT2 的问题 TCP状态迁移 大家对netstat -a命令很熟悉,但是,你有没有注意到STATE一栏呢,基 ...

  7. C# .NET Winform等程序一运行就闪退 不同环境 换到其他电脑,例如XP之类的解决方法。

    我现在写标题就跟写tag一样,方便大家索引. 有时候经常会把.NET写的程序拿到其他客户机,发现直接闪退,啥也不报错..那就很蛋疼,又不可能去装IDE来检测是吧,网上还有很多说啥看系统日志的,什么图标 ...

  8. zabbix3.0.4关于java服务端程序内存溢出的处理

    关于java服务端程序内存溢出的处理 java服务端程序内存溢出会产生jvm.log文件,此时程序会挂掉,无法正常处理业务,需要重启服务 思路: 当存在jvm.log这个文件的时候则触发clean_j ...

  9. C#面向对象(封装)

    以上就是面向对象的封装和初始化:

  10. OneNET麒麟座应用开发之九:与SD卡通讯并保存数据

    由于需要记录的数据量比较大,而且有些时候,有的用户不方便实时上传数据,所以要求使用SD卡存储数据然后人工收取上传.为此我们选择了一种通用的SD卡读写器. 1.读卡器简介 该读卡器整合 SD 卡规范和 ...