angular4 组件生命周期
红色的被调用一次,绿色的会被调用多次,变化检测中的四个方法和组件初始化中的四个方法是一样的。
组件初始化:前面四个初始化属性,后面四个渲染视图。
变更检测:使属性值和页面展示保持一致。
组件生命周期顺序:
1.constructor:构造器函数,实例化对象,并用于注入服务
2.ngOnChanges:初始化 输入属性,检测到@Input输入数据变化时执行,首次触发发生在ngOnInit前。注意对象的属性发生变化时监听不到
3.ngOnInit:初始化 除输入属性之外其他的属性,通常会设置一些初始值(如果初始化的逻辑需要依赖@Input输入属性,那就一定要写在ngOnInit中,而不要写在构造函数中)
4.ngDoCheck:组件变更检测时执行
5.ngAfterContentInit:投影内容 初始化到组件之后执行
6.ngAfterContentChecked:投影内容 变更检测之后执行
7.ngAfterViewInit:视图 初始化之后执行
8.ngAfterViewChecked:视图 发生变化检测之后执行,这个可以用来保证用户视图的及时更新
9.ngOnDestroy:组件注销时的清理工作,通常用于移除事件监听,退订可观察对象等
1.onChanges
当@input属性(输入属性)发生变化时,会调用;当输入属性为对象时,当对象的属性值发生变化时,不会调用,当对象的引用变化时会触发。
例子:
child.ts代码片段:
@Input()
greeting: string;
@Input()
user:{name:string};
如上,greeting改变会触发ngOnChanges事件,而user.name改变不会触发ngOnChanges事件,因为greeting是字符串是不可变对象(每次值改变的时候都会创建一个新的字符串,然后把引用指向新的字符串),而user是可变对象,修改姓名的值的时候并没有改变user对象的引用。那么怎么监控可变对象呢,用doCheck。
2.变更检测机制
angular使用package.json文件中dependencies引入的zone.js来实现变更检测机制。
保证属性的变化和页面的变化是一致的,浏览器中发生的任何变化都会触发变更检测机制,比如点击按钮,输入数据。。。
Default策略:默认,不管变更发生在哪个组件上,zone都会检测整个组件树。
OnPush策略:如果有某个组件声明自身的策略为onPush,只有当这个组件的@input输入属性发生变化时,zone才会检测该组件及其子组件。
例子:
变更检测机制的实现需要实现DoCheck接口中的ngDoCheck()方法。页面上随便一个点击都会触该方法,比如点击输入框等,所有在使用上要非常小心。
import {Component, OnInit,Input,,DoCheck} from '@angular/core'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'] }) export class ChildComponent implements DoCheck{ constructor() {} ngDoCheck():void{ //组件变更时触发 } }
需要注意的是,当变更检测机制发生的时候,不光是doCheck()方法,所有含check关键字的方法(包括ngAfterContentChecked(),ngAfterViewChecked())都会被调用。所以这些方法也都要非常小心的使用。
3.view钩子
1.父组件调用子组件的方法
child.component.ts
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.css'] }) export class ChildComponent implements OnInit { constructor() { } ngOnInit() {} //子组件方法 greeting(name:string){ console.log("hello"+name); } }
方法一:在模板中通过对子组件标签的模板变量( #变量名)直接调用用子组件方法:
parent.component.html
<!--定义模板变量child1--> <app-child #child1></app-child> <app-child #child2></app-child> <!--在模板中调用子组件greeting()方法--> <button (click)="child2.greeting('Jerry')"></button>
方法二:在组件类中通过@ViewChild装饰器声明子组件的对象,然后调用子组件对象中的方法
parent.component.ts
import { Component,ViewChild } from '@angular/core'; import {ChildComponent} from "./child/child.component"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { //通过@ViewChild获取的子组件对象 @ViewChild("child1") child1:ChildComponent constructor(){} ngOnInit():void{ this.child1.greeting("Tom");//调用方法 } }
2.AfterViewInit和AfterViewChecked
在整个组件模板的内容显示完成,即都已经呈现给用户看之后,会调用这两个方法;
初始化方法AfterViewInit()优先被调用,而且只调用一次,AfterViewChecked后调用;
如果存在子组件,则先调用子组件的,再调用父组件的;
不要在这两个方法中去改变视图中绑定的东西,如果想改变也要写在一个setTimeout里边,如下:
例子中,整个视图已经初始化完毕了,又要去修改与视图有绑定的message,会报错,
message:string; ngAfterViewInit(): void { console.log("父组件的视图初始化完毕"); this.message="Hello"; }
改成:
ngAfterViewInit(): void { console.log("父组件的视图初始化完毕"); setTimeout(()=>{ this.message="Hello"; },0); }
在javaScript另一个运行周期去运行
4.ngContent
ngContent指令用来将父组件模板中的任意html片段投影到子组件中。
例子:
child.html
<div> <h2>子组件</h2> <ng-content select=".header"></ng-content> <ng-content select=".footer"></ng-content> </div>
parent.html
<div> <h2>父组件</h2> <app-child> <div class="header">页头</div> <div class="footer">页脚</div> </app-child> </div>
如上,子组件通过<ng-content>指令的select属性,分别将父组件的页头和页脚两个div投影到子组件中。
ngAfterContentInit和ngAfterContentChecked在投影的模板被渲染之后调用。
5.ngOnDestroy
从A路由跳到B路由后,A路由对应的组件会执行ngOnDestroy