Angular中的变更检测机制是当component状态有变化的时候,angular都能检测到这些变化,并且能够将这些变化反应到页面上。
作为框架,这个在我们看来是理所应当的,其实在angular内部涉及到很多复杂的操作,包括:变化检测、脏数据检查、数据绑定、单向数据流、更新DOM、NgZone等。
我们知道Angular应用和Vue/React本质上是一棵组件树,变更检测都是沿着组件树从root组件开始至上而下执行的,所以上面变更检测机制涉及的很多操作和属性都是组件级的,下面分别说明:
触发变更检测的时机
- DOM事件:页面的click、submit、mousedown……
- XHR:从后端服务器拿到数据
- Timers:setTimeout()、setInterval()
单项数据流
假设其中某个组件中触发了变更检测,就会从根组件开始,从上至下排着检测一遍,并且已经检测完的组件,不允许再被子组件修改
在子组件中修改了值,虽然会报错,但仍显示的是变更之后的结果。
Expression has changed after it was checked.
onPush策略
如果设置了onPush变更检测策略,则变更检测器仅在显式调用它,或由
@Input
引用的变化或触发事件处理程序时运行,以提高性能。
- onPush策略后触发变更检测的时机
- DOM事件:页面的click、submit、mousedown……等dom事件
- 组件的@Input引用发生变化
- Observable订阅事件,同时设置了Async pipe
- 手动使用ChangeDetectorRef.markForCheck()、ApplicationRef.tick()方法
阻断了一个组件的变更检测后,它和它的子组件都不会检测了
ChangeDetectorRef
从变更检测树中添加或移除视图、初始化变更检测并显示地把这些视图标记为脏的,意思是它们变了,需要重新渲染。
constructor(private cdr:ChangeDetectorRef){
this.cdr.detach();
}
- detach()
从变更检测树中分离开视图,已分离的视图在重新附加上去之前不会检查。与detectChanges()结合使用,可以实现局部变更检测
- reattach()
把以前分离开的视图重新附加到变更检测树上。
- markForCheck()
在onPush中,通过定时器触发变更检测,要引入ChangeDetectorRef,使用markForCheck将视图显示地标记为已更改,以便它再次进行检查。
constructor(private cdr:ChangeDetectorRef){}
ngOninit(){
setTimeout(() => {
this.cdr.markForCheck();
})
}
- detectchanges()
检查该视图与子视图
- 至此,我们在后面的创建的组件中,统一都采用OnPush策略,犹如
Ant Design of Angular
官网描述的一般:支持 OnPush 模式,性能卓越。
@Component({
changeDetection:ChangeDetectionStrategy.OnPush
})
- 创建组件时,直接声明为onPush模式
ng g c components -s -skipTests -c OnPush