钩子的调用顺序:
consttructor -> ngOnChanges -> ngOnInit -> ngDoCheck ->
ngAfterContentInit -> ngAfterContentChecked -> ngAfterViewInit ->
ngAfterViewChecked -> ngOnDestroy
ngOnchanges
- 当父组件初始化或者修改子组件的输入属性时调用;
- 如果一个组件没有输入属性,那么永远不会调用该钩子;
- 首次调用一定在ngOnInit之前;
- 可以被多次调用;
可变对象和不可变对象
123let val = 'hello';val = 'hello world';`
- ‘hello’在内存中被创建后赋值给val;
- ‘hello world’在内存中被创建后赋值给val;
无论val如何变化,’hello’在内存中的地址始终保持不变。字符串是不可变对象。
|
|
- 在内存中创建了user,name属性指向’roy’所在的地址;
- 在内存中创建了’joy’,地址赋值给了user的属性name;
- name发生了变化,但是user的地址始终不变;也就是说由于属性的改变,user(对象)是可变对象;
ngOnChanges触发机制
父组件
|
|
|
|
子组件
|
|
|
|
以上代码结果表现为:
- 当输入问候语hello时,①被触发。原因:由于字符串是不可变对象,然后输入后却发生了变化,故调用ngOnChanges;
- 修改名字name时,①不触发。原因:由于修改的是可变对象(user的属性虽然变化了,但是user在内存中的地址依然没变),故不会触发ngOnChanges;
- 当修改message时,①不处罚。原因:ngOnChanges钩子只针对输入属性,message为非输入属性;
虽然修改可变对象不会触发ngOnChanges钩子,但是子组件上的值已然发生了变化。这是由于Angular的变更检测机制仍然不活了组件中每个对象的属性的变化。
ngDoCheck
在这里,需要先重点说一下Angular的变更检测机制
- 在Angular1.x中,任何原生事件都不会触发脏检查,必须使用NG事件才有效。如果使用了原生事件,你需要指定$apply()和$digest()来告诉Angular使用变更检测去处理它。
在Angular2中,变更检测机制是由zone.js提供的,保证组件的属性的变化和页面的变化是同步的。
- 浏览器中发生的任何事件都会触发变更检测,如:点击事件、输入数据;
- 可以随意使用任何原生事件;
变更检测机制仅仅是将组件属性的改变反应到模块上,它并不会去改变组件属性的值。
变更检测
- 每一个组件生成属于他们的变更检测器。当属性发生变化时,变更检测器指定的变更检测机制(default和Onush)就会响应,并判断是否需要更新模块;
- 变更检测策略:
- Default策略:采用Default策略的组件,无论组件树的哪个地方发生变化,都会被检测;检测顺序:根组件->子组件->孙组件;
- Onpush策略:只有当组件的输入属性发生变化,变更检测机制才会检测该组件及其子组件;
对可变对象的检测:ngDoCheck
|
|
- 组件中只要有事件发生,ngDoCheck就会被调用①,比如:在两个文本框之间切换;
- 由于①的原因,ngOnCheck非常容易被调用,使用时需要小心。对于ngOnCheck的实现要非常高效和轻量,否则容易因其性能问题;
- 在Default策略下,每次变更检测,组件树中所有带check后缀的钩子都会被调用;
view钩子
子组件
123456getVal(val:string):void{console.log(val);}①ngAfterViewInit():void{}②ngAfterViewChecked():void{}父组件
1234<div>我是父组件</div><app-child #child1></app-child><app-child #child2></app-child><button (click)="child2.getVal('joy')"></button>
|
|
钩子调用顺序:①->②->③->④
- 很明显,子组件必须在父组件之前组装完毕,init方法只会被调用一次;
- 在变更检测周期中禁止修改属性,如果必须修改需要在子线程中操作;12345678ngAfterViewInit():void{this.val = "hhahh"; //严重错误//解决方案setTimeout(()=>{this.val = "hahha";},0)}
ngContent指令
- 一般写法
子组件
父组件
- 多个投影的写法
父组件1234567<div><div>父组件</div><app-child><div class="firstPart">这是第一块内容</div><div class="secondPart">这是第二块内容</div></app-child></div>
子组件
ngAfterContentInit、ngAfterContentChecked
在投影内容组装完成之后调用、检测之后调用;
执行顺序:ngAfterContentInit -> ngAfterContentChecked -> ngAfterContentInit -> ngAfterContentChecked -> ngAfterViewInit
与ngAfterView的钩子不同,在ngAfterContent的钩子中可以改变属性的值
ngOnDestory
- 当组件销毁的时候调用该钩子(配合路由使用);
- 往往在该钩子中处理“反订阅一个流”、“清除定时器”等操作;
总结
组件初始化
①constructor
②ngOnChanges
③ngOnInit
④ngDoCheck
⑤ngAfterContentInit
⑥ngAfterContentChecked
⑦ngAfterViewInit
⑧ngAfterViewChecked
⑨ngOnDestory
组件销毁
⑨ngOnDestory
变更检测
②ngOnChanges
④ngDoCheck
⑥ngAfterContentChecked
⑧ngAfterViewChecked
每一个组件都会经历三个阶段:初始化、变更检测、销毁
当通过路由激活一个组件(按先后顺序):
组件初始工作:
启动constructor,提供所需实例化的对象
如果有输入属性,启动ngOnChanges检测输入属性的变更
启动ngOnInit,初始化一般数据
启动ngDoCheck,进行一次变更检测
开始组装组件:
当组件中的投影内容被组装时,调用ngAfterContentInit
启动ngAfterContentChecked,对投影内容检测
当整个组件被组装时,调用ngAfterViewInit
启动ngAfterViewChecked,对整个组件检测
组件变更:
当有事件发生(用户操作),启动ngDoCheck进行变更检测
处理变更,如果需要更新组件,启动ngAfterContentChecked、ngAfterViewChecked;如果输入属性发生变化,启动ngOnChanges
路由事件发生,启动ngOnDestory销毁组建,从头开始初始化一个组件;