Angular 个人深究(四)【生命周期钩子】
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提供了眾多的生命周期的鉤子,讓我們能夠很好的在發生變化的時候進行處理。
轉載于:https://www.cnblogs.com/primadonna/p/9803083.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Angular 个人深究(四)【生命周期钩子】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑上每月符号怎么打(电脑怎么打几点的符
- 下一篇: (兔子繁殖问题)有一对兔子,从出生后第3