Angular25 组件的生命周期钩子
1 生命周期钩子概述
组件共有9个生命周期钩子
1.1 生命周期的执行顺序
技巧01:测试时父组件传递对子组件的输入属性进行初始化操作
import { Component, Input, SimpleChanges, OnInit, OnChanges, DoCheck, AfterContentChecked, AfterViewInit, AfterContentInit, AfterViewChecked, OnDestroy } from '@angular/core'; let logIndex : number = 1; @Component({
selector: 'life',
templateUrl: './life.component.html',
styleUrls: ['./life.component.scss']
})
export class LifeComponent implements OnInit, OnChanges, DoCheck,
AfterContentInit, AfterContentChecked, AfterViewInit,
AfterViewChecked, OnDestroy { @Input()
name : string; logIt(msg : String) {
console.log(`#${logIndex++} - ${msg}`);
} constructor() {
this.logIt("name属性在constructor里面的值为: " + this.name);
} ngOnInit() {
this.logIt("ngOnInit");
} ngOnChanges(changes : SimpleChanges){
this.logIt("name属性在ngOnChanges里面的值为: " + this.name);
} ngDoCheck() {
this.logIt("ngDoCheck");
} ngAfterContentInit() {
this.logIt("ngAfterContentInit");
} ngAfterContentChecked() {
this.logIt("ngAfterContentChecked");
} ngAfterViewInit() {
this.logIt("ngAfterViewInit");
} ngAfterViewChecked() {
this.logIt("ngAfterViewChecked");
} ngOnDestroy() {
this.logIt("ngOnDestroy");
} }
TS
2 ngOnChanges
2.1 可变对象和不可变对象
在JavaScript中string类型是不可变对象,自定义的对象是可变对象;例如:
var a = "hello"; -> 变量a中存储的是字符串“hello”对应的内存地址
a = "warrior"; -> 变量a中存储的是字符串“warrior”对应的内存地址
结论:将不同的字符串赋值给同一个变量时,变量的值会发生改变;所以string类型是不可变类型【PS: 跟Java一样】
var user : {name : string} = {name : "warrior"}; -> 变量user中存储的是对象{name : "warrior"}对应的内存地址
user.name = "fury"; -> 变量user中还是存储的对象{name : "fury”}对应的内存地址,而且{name : "warrior"}和{name : "fury”}是同一个对象,所以他们的内存地址相同,故变量user中存储的值没有发生改变,改变的仅仅是变量user所指向的那个对象中的内容而已
结论:修改一个对象的内容并不会改变这个对象的内存地址,所以对象是可变对象
2.2 ngOnChanges
当输入属性的值发生改变时就会触发ngOnChanges
技巧01:如果输入属性的类型是一个对象时,需要区分是可变对象还是不可变对象,只有不可变对象时才会触发ngOnChanges;总之记住只要输入属性的值发生改变才会触发ngOnChanges
技巧02:ngOnChanges方法需要传入一个 SimpleChanges 类型的参数,可以利用该参数来查看输入属性改变前后的值,以及是否是初次赋值
技巧03:JSON.stringfy() 方法在将数据转化成JSON时可以进行格式化,例如
ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
}
2.2.1 子组件代码
<div class="panel panel-primary">
<div class="panel-heading">子组件</div>
<div class="panel-body">
<p>问候语:{{greeting}}</p>
<p>姓名:{{user.name}}</p>
<p>年龄:{{age}}</p>
<p>
消息:<input type="text" name="message" [(ngModel)]="message" />
</p>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, EventEmitter, Output, Input, OnChanges, SimpleChanges } from '@angular/core'; @Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit, OnChanges { @Input()
greeting : string; @Input()
user : {name : string}; @Input()
age : number; message : string; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
setInterval(() => {
this.currentDate = new Date();
}, 1000);
} ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
} }
TS
2.2.2 父组件代码
<div class="panel panel-primary">
<div class="panel-heading">父组件</div>
<div class="panel-body">
<p>
问候语:<input type="text" name="greeting" [(ngModel)]="greeting" />
</p>
<p>
姓名:<input type="text" name="name" [(ngModel)]="user.name" />
</p>
<p>
年龄:<input type="number" [(ngModel)]="age" />
</p>
<child [greeting]="greeting" [user]="user" [age]="age"></child>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit } from '@angular/core'; @Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit { greeting : string = "Hello"; user : {name : string} = {name : "王杨帅"}; age : number = 8; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
setInterval(() => {
this.currentDate = new Date();
}, 1000);
} }
TS
2.2.3 效果图
3 ngDoCheck
3.1 变更检测机制
angular中由zone.js去负责监听浏览器中所有异步事件(事件[单击、双击]、定时器、ajax)来保证模板和组件属性的变化时同步的;
只要zone.js检测到有异步事件发生时所有监测机制是默认检测机制的组件都会触发ngDoCheck
应用:当输入属性的类型是可变对象时,即使可变对象的内容发生了变化 ngOnchanges 也不会被调用,但是 ngDoCheck 会被调用;所以我们可以利用 ngDoCheck 来检测可变对象的变化
坑01:zone.js检测到任何一个组件有异步事件发生都会让所有采用默认变更检测机制的组件执行 ngDoCheck,所以 ngDoCheck 方法要慎用,而且逻辑不能太复杂,不然会影响性能
3.2 变更检测策略
3.2.1 default
angular默认的变更检测策略
如果所有的组件都是默认的变更检测策略,那么当一个组件发生改变时angular会对所有的组件进行变更检查
3.2.2 onPush
待更新...
3.3 代码汇总
3.3.1 父组件代码
<div class="panel panel-primary">
<div class="panel-heading">父组件</div>
<div class="panel-body">
<p>
问候语:<input type="text" name="greeting" [(ngModel)]="greeting" />
</p>
<p>
姓名:<input type="text" name="name" [(ngModel)]="user.name" />
</p>
<p>
年龄:<input type="number" [(ngModel)]="age" />
</p>
<child [greeting]="greeting" [user]="user" [age]="age"></child>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit } from '@angular/core'; @Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit { greeting : string = "Hello"; user : {name : string} = {name : "王杨帅"}; age : number = 8; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
} }
TS
3.3.2 子组件代码
<div class="panel panel-primary">
<div class="panel-heading">子组件</div>
<div class="panel-body">
<p>问候语:{{greeting}}</p>
<p>姓名:{{user.name}}</p>
<p>年龄:{{age}}</p>
<p>
消息:<input type="text" name="message" [(ngModel)]="message" />
</p>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, EventEmitter, Output, Input, OnChanges, SimpleChanges } from '@angular/core';
import { DoCheck } from '@angular/core/src/metadata/lifecycle_hooks'; @Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit, OnChanges, DoCheck { @Input()
greeting : string; @Input()
user : {name : string}; oldName : string; // 存储上一次的值
changeNum : number = 0; // 非本组件触发ngDoCheck方法的次数
changeDetected : boolean; // 是否是本组件触发ngDoCheck方法 @Input()
age : number; message : string; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
} ngOnChanges(simpleChanges : SimpleChanges) {
console.log(JSON.stringify(simpleChanges, null, 2));
} ngDoCheck() : void { // 如果检测到是本组件的输入属性变化
if (this.user.name !== this.oldName) {
this.changeDetected = true;
console.log("ngDoCheck: user.name 从 "+ this.oldName +" 变成了 "+ this.user.name +" ");
this.oldName = this.user.name;
} if (this.changeDetected) {
this.changeNum = 0; // 本组件触发的ngDoCheck就进行清零操作
} else { // 非本组件触发的ngDoCheck方法就进行加一操作
this.changeNum = this.changeNum + 1;
console.log("ngDoCheck: user.name没有变化,ngDoCheck方法被调用了" + this.changeNum + "次");
} this.changeDetected = false; } }
TS
3.3.3 效果展示
4 ngAfterViewInit 和 ngAfterViewChecked
4.1 父组件调用子组件的方法
这两个钩子是在组件的模板已经完全组装好后才会被调用,所以在这两个钩子中不能修改和模板相关的数据信息
4.1.1 利用模板变量实现
在父组件的视图模板中为子组件添加一个模板变量,然后在父组件中就可以利用这个模板变量来调用子组件的方法了
4.1.2 利用子组件的实例实现
在父组件的视图模板中为子组件添加一个模板变量,然后在组件类中利用@ViewChild来实例化一个子组件实例,再通过这个实例在父组件类中调用子组件的方法
4.2 调用顺序
4.2.1 组件中这两个钩子的调用顺序
组件被加载 -> 视图初始化钩子
-> 视图变更检测钩子
-> 视图变更检测钩子
技巧01:组件被调用时先要执行视图初始化钩子,再执行视图变更检测钩子,而且视图初始化钩子只会被调用一次
4.2.2 父子组件中的调用顺序
父组件被加载 -> 子组件被加载 -> 子组件视图初始化钩子 -> 父组件视图初始化钩子
-> 子组件视图变更检测钩子 -> 父组件视图变更检测钩子
-> 子组件视图变更检测钩子 -> 父组件视图变更检测钩子
技巧01:先执行执行子父组件的视图初始化钩子 -> 再子父组件的视图变更检测钩子 -> 循环调用子父组件的视图变更检测钩子
技巧02:视图变更检测钩子的调用是先调用子组件的再调用父组件的
技巧03:视图初始化钩子的调用是先调用子组件的再调用父组件的
坑01:这两个钩子都是在视图组装好之后触发的
4.2.3 修改数据
视图初始化钩子和视图变更检测钩子中不能修改和视图相关的数据信息 -> 可以利用一个定时器来实现,因为利用定时器可以让操作在另外一个JavaScript运行周期中运行
4.2.4 代码汇总
》父组件
<div class="panel panel-primary">
<div class="panel-heading">父组件</div>
<div class="panel-body"> <child #child1></child>
<child #child2></child> <button (click)="child2.greeting('Zeus')">调用子组件child2的方法</button>
<br /> <p>{{info}}</p>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChildComponent } from './child/child.component';
import { AfterViewInit, AfterContentChecked } from '@angular/core/src/metadata/lifecycle_hooks'; @Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit, AfterViewInit, AfterContentChecked { @ViewChild("child1")
child1 : ChildComponent; info : string; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000); setInterval(() => {
this.child1.greeting("Warrior");
}, 5000);
} ngAfterViewInit() : void {
console.log("父组件视图初始化完毕");
// this.info = "good boy"; // 视图初始化钩子中不能修改和视图相关的数据信息
// setTimeout(() => { // 上面一行代码的解决办法
// this.info = "王杨帅";
// }, 5000);
} ngAfterContentChecked() : void {
console.log("父组件视图变更检测完毕")
this.info = "good boy"; // 视图变更检测钩子中可以修改和视图相关的数据信息
} }
TS
》子组件
<div class="panel panel-primary">
<div class="panel-heading">子组件</div>
<div class="panel-body"> </div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, DoCheck, EventEmitter, Output, Input, OnChanges, SimpleChanges, AfterViewInit, AfterContentChecked } from '@angular/core'; @Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit, AfterViewInit, AfterContentChecked { currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
} greeting(info : string) : void {
console.log("Hello " + info);
} ngAfterViewInit() : void {
console.log("子组件视图初始化完毕");
} ngAfterContentChecked() : void {
console.log("子组件视图变更检测完毕")
} }
TS
5 ngAfterContentInit 和 ngAfterContentChecked
5.1 内容投影
5.1.1 需求
如何将父组件的数据传递到子组件
5.1.2 方案
》利用路由解决
》利用子组件的输入属性解决
》利用服务解决
》利用投影解决 【推荐】
5.1.3 投影编程步骤
》子组件的视图
在子组件的视图中添加 ng-content 标签
》父组件视图
在父组件中添加子组件标签,并在子组件标签内编写需要进行投影的代码;当系统运行后会自动将需要投影的代码投影到子组件的 ng-content 标签里面
技巧01:ng-content 仅仅是一个占据位置的标签,系统运行后投影的内容会代替 ng-content 的位置
》如何实现多个投影
为子组件的每个 ng-content 标签添加 select 属性
技巧01: select 的属性值格式是 “.属性值”
在父组件视图中使用子组件的内部利用class为其指定投影到那个 ng-content
》代码汇总
<div class="panel panel-primary">
<div class="panel-heading">子组件</div>
<div class="panel-body">
<div style="border: 1px solid red;">
<ng-content select=".red"></ng-content>
</div>
<hr />
<div style="border: 1px solid green">
<ng-content select=".green"></ng-content>
</div>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
子组件HTML
<div class="panel panel-primary">
<div class="panel-heading">父组件</div>
<div class="panel-body"> <child>
<div class="red">
我是父组件传过来的数据【放到子组件的红框中】
</div>
<div class="green">
我是父组件传过来的数据02【放到子组件的绿框中】
</div>
</child> </div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
父组件HTML
5.2 执行顺序
5.2.1 同一组件中这两个钩子的调用顺序
先调用投影内容初始化钩子 -> 再调用投影内容变更检测钩子
5.2.2 父子组件中的这两个钩子的调用顺序
先调用父组件中的钩子 -> 再调用子组件的的钩子
技巧01:投影内容相关的两个钩子在父子组件中的调用顺序和视图相关的两个钩子在父子组件中的调用顺序恰恰相反
5.2.3 修改数据
投影相关的两个钩子可以修改和视图相关的数据信息
愿意:投影相关的两个钩子是在视图组装完毕之前调用的,而视图相关的两个钩子是在视图组装完毕后调用的
5.2.4 代码汇总
》父组件代码
<div class="panel panel-primary">
<div class="panel-heading">父组件</div>
<div class="panel-body"> <child>
<div class="red">
我是父组件传过来的数据【放到子组件的红框中】
</div>
<div class="green">
我是父组件传过来的数据02【放到子组件的绿框中】
</div>
</child> </div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, ViewChild } from '@angular/core';
import { ChildComponent } from './child/child.component';
import { AfterViewInit, AfterContentChecked, AfterContentInit, AfterViewChecked } from '@angular/core/src/metadata/lifecycle_hooks'; @Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit,
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked { @ViewChild("child1")
child1 : ChildComponent; currentDate : Date; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
} ngAfterContentInit() : void {
console.log("父组件投影内容初始化完毕");
} ngAfterContentChecked() : void {
console.log("父组件投影内容变更检测完毕");
} ngAfterViewChecked() : void {
console.log("父组件视图内容变更检测完毕");
} ngAfterViewInit() : void {
console.log("父组件视图初始化完毕");
} }
TS
》子组件代码
<div class="panel panel-primary">
<div class="panel-heading">子组件</div>
<div class="panel-body">
<div style="border: 1px solid red;">
<ng-content select=".red"></ng-content>
</div>
<hr />
<div style="border: 1px solid green">
<ng-content select=".green"></ng-content>
</div>
<p>{{info}}</p>
</div>
<div class="panel-footer">{{currentDate | date : "yyyy-MM-dd HH:mm:ss"}}</div>
</div>
HTML
import { Component, OnInit, DoCheck,AfterViewChecked, EventEmitter, AfterContentInit, Output, Input, OnChanges, SimpleChanges, AfterViewInit, AfterContentChecked } from '@angular/core'; @Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit,
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked { currentDate : Date; info : string; constructor() { } ngOnInit() {
this.currentDate = new Date();
// setInterval(() => {
// this.currentDate = new Date();
// }, 1000);
} ngAfterContentInit() : void {
console.log("子组件投影内容初始化完毕");
// this.info = "warrior"; // 投影内容初始化完毕时可以修改视图相关数据信息
} ngAfterContentChecked() : void {
console.log("子组件投影内容变更检测完毕");
this.info = "fury"; // 投影内容变更检测完毕时可以修改视图相关数据信息
} ngAfterViewChecked() : void {
console.log("子组件视图内容变更检测完毕");
// this.info = "warrior";
} ngAfterViewInit() : void {
console.log("子组件视图初始化完毕");
} }
TS
5.2.5 效果展示
Angular25 组件的生命周期钩子的更多相关文章
- Vue_(组件)实例生命周期钩子
Vue生命周期中文文档 传送门 Vue生命周期:Vue实例从创建到销毁的过程,称为Vue的生命周期: Vue生命周期钩子:又称为Vue生命周期钩子方法/函数,是Vue为开发者提供的方法,我们可以通过这 ...
- 通俗易懂了解Vue组件的生命周期
1.前言 在使用vue2.0进行日常开发中,我们总有这样的需求,我就想在页面刚一加载出这个表格组件时就发送请求去后台拉取数据,亦或者我想在组件加载前显示个loading图,当组件加载出来就让这个loa ...
- Vue项目的创建、路由、及生命周期钩子
目录 一.Vue项目搭建 1.环境搭建 2.项目的创建 3.pycharm配置并启动vue项目 4.vue项目目录结构分析 5.Vue根据配置重新构建依赖 二.Vue项目创建时发生了什么 三.项目初始 ...
- [Vuejs] 组件 v-if 和 v-show 切换时生命周期钩子的执行
v-if 初始渲染 初始值为 false 组件不会渲染,生命周期钩子不会执行,v-if 的渲染是惰性的. 初始值为 true 时,组件会进行渲染,并依次执行 beforeCreate,created, ...
- Angular组件生命周期——生命周期钩子
生命周期钩子介绍: 1.ngOnChange:响应组件输入值发生变化时触发的事件. 2.ngOnInit:用于数据绑定输入属性之后初始化组件,在第一次ngOnChange之后被调用. a. 组件构造后 ...
- Vue ---- 组件文件分析 组件生命周期钩子 路由 跳转 传参
目录 Vue组件文件微微细剖 Vue组件生命周期钩子 Vue路由 1.touter下的index.js 2.路由重定向 3.路由传参数 补充:全局样式导入 路由跳转 1. router-view标签 ...
- Vue 组件生命周期钩子
Vue 组件生命周期钩子 # 1)一个组件从创建到销毁的整个过程,就称之为组件的生命周期 # 2)在组件创建到销毁的过程中,会出现众多关键的时间节点, 如: 组件要创建了.组件创建完毕了.组件数据渲染 ...
- 简单记录一下vue生命周期及 父组件和子组件生命周期钩子执行顺序
首先,vue生命周期可以用下图来简单理解 当然这也是官方文档的图片,详细的vue周期详解请参考这里 然而当同时存在父子组件的时候生命周期钩子是如何执行的呢? 请看下文: 加载渲染过程父beforeCr ...
- Vue生命周期 钩子函数和组件传值
Vue生命周期 钩子函数 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听.编译模板.将实例挂载到 DOM 并在数据变化时更新 DOM 等. 同时在这个过程中也会运行一 ...
随机推荐
- 【前端】XHTML入门笔记
教程/XHTML 模块/XHTML 标准属性/XHTML 事件属性 XHTML 指可扩展超文本标签语言(EXtensible HyperText Markup Language). XHTML 元素必 ...
- 【占位】HihoCoder1317 跳舞链
跳舞链 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近遇到一个难题,他需要破解一个棋局. 棋局分成了n行,m列,每行有若干个棋子.小Ho需要从中选择若干行使得 ...
- DbEntry 访问Access2010数据库
在用DbEntry访问Access2010的时候报错如下: 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序 解决方案: 安装AccessDatabaseEngine组 ...
- rabbitmq java
package com.enniu.rabbitmq; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.AMQP.BasicPr ...
- 调试VBA程序常用方法
在中断模式下(ctrl+Break键),可以做: 1.执行 工具----选项----编辑器----勾选“自动显示数据提示” 则当用鼠标悬停在变量或表达式上时,会出现提示窗口,显示其名称和值! 2 ...
- oracle系统表的查询
oracle查询用户下的所有表 select * from all_tab_comments -- 查询所有用户的表,视图等select * from user_tab_comments -- 查 ...
- 异常mongodb:Invalid BSON field name XXXXXX:YYYYY.zz
1.本周遇到这个问题. 定位到发现一个很神奇的现象上面的结构无法顺利以map的key值存入mongodb里面. 而且到线上才发现这个问题. 而且是部分用户才会出现这样的情况 大部分人的该数据是这样的 ...
- Backit轻松为您的网站创建备份
随着日益复杂,备份数据,已成为一个经验法则,为维护数据库和重要的企业信息化的大公司和企业.在一个不幸的灾难的情况下,很多企业无法恢复他们的数据,这进一步增加了在他们对所有盈利和亏损的同时,作为他们的电 ...
- pytest命令行选项
-m 标记 代码加一个装饰器:@pytest.mark.run_bbc_test,命令行添加 -m run_bbc_test,执行带@pytest.mark.run_bbc_test的测试用例: -k ...
- Git 学习小问题记录
最近一直使用Git在管理代码,但是的确不规范,今天开始恶补Git常用命令.实际今天的任务是需要从master牵出一条branch.心想着这个简单只补一下创建分支以及merge的这边的命令就可以了,于是 ...