Angularjs与Angular对脏检查机制的理解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/friend_ship/article/details/81742436

“脏检查”是Angular中的核心机制之一,它是实现双向数据绑定、MVVM模式的重要基础。

AngularJS常用函数:$apply,$watch及$digest

$digest是一个内部函数,正常的应用代码中是不应该直接调用它的。要想主动触发它,就要调用scope.$apply函数,它是触发Angular“脏检查机制”的常用公开接口。$digest循环实际上包括两个while循环。分别是:处理$evalAsync的异步运算队列,处理$watch的watchers队列。当该循环触发时,它会遍历当前$scope及其所有子$scope上已注册的所有watchers函数。遍历一遍所有watcher函数成为一轮脏检查。执行完一轮脏检查,如果任何一个watcher所监听的值改变过,那么就会重新再进行一轮脏检查,直到所有的watcher函数都报告其所监听的值不改变了。当循环结束时,才把模型的变化结果更新到DOM中去。这样做为了,防止频繁更新DOM属性。

通俗理解脏检查:表达式{{aaa.x}},AngularJS不仅会渲染该数据,还会为该特定值创建一个观察程序。之后,只要程序发生任何事情,AngularJS就会检查该观察过程中的值是否更改。如果有,重新呈现表达式。运行这些观察者的过程称为脏检查。

Angular处理脏检查引用的是Zone.js

NgZone是一个带有基于Observables的附加api的分叉区域

想要数据发生变化应用到页面上,首先需要检测数据的变化,数据变化一般发生异步事件中,如:

浏览器事件,eg:click,submit

setTimeout和setInterval

XHR,从远程服务器获取数据……

谁通知了Angular?

class ApplicationRef {

    changeDetectorRefs:ChangeDetectorRef[] = [];

    // applicationRef在构造器中监听onTurnDone事件
    constructor(private zone: NgZone) {
        this.zone.onTurnDone
        .subscribe(() => this.zone.run(() => this.tick()))
    } 

    // tick函数遍历所有的探测器的接口/对象  对其执行检测
    tick() {
        this.changeDetectorRefs
        .forEach((ref) => ref.detectChanges());
        }
    }

}

变更检测是如何进行的呢?

每一个组件都有属于自己的变更检测器(change detector)

变更检测树:有向图  单向数据流   始终都是由上而下执行更改检测

默认情况下,Angular是保守的,每次都会检查每个组件

变更检测策略

enum ChangeDetectionStrategy {

        OnPush: 0 // 仅在输入已更改时才对视图进行更改检测。当输入属性不变时,Angular可以跳过整个变更检测树

         Default: 1  // 默认策略,其中更改检测是自动的,直到明确停用

}

NgDoCheck钩子和变更检测

更新子组件的属性

调用位于子组件中的NgDoCheck生命周期钩子

更新当前组件的DOM

向子组件执行变更检测

NgDoCheck作用:配合markForCheck和OnPush

export class AppComponent {
    @Input() data;
    
    
    public id;

    constructor(private cdr: ChangeDetectorRef){}

    ngOnChanges() {
        // 当data改变时,更新id
        this.id = this.data.id;
    }
    

    ngDoCheck() {
        // 在ngDoCheck中检测data这个object的属性是否更改
        if( this.id !== this.data.id ) {
            this.cdr.markForCheck();
        }
    }
    
}
@Component({
    template:'{{num}}'
    changeDetection: ChangeDetectionStrategy.OnPush
})

export class NumComponent implements OnInit {
    @Input() addItem: Observable<any>;
    
    num = 0;

    constructor(private cdr: ChangeDetectorRef){}
    ngOnInit() {
        this.addItem.subscribe(()=>{
            this.num++;
            this.cdr.markForCheck();//人为通知Angular进行检测
        })
    }
}

Question?

为什么在OnPush策略下,即使组件没有属性更新,ngOnCheck钩子仍然被调用?

存在一种可能是子组件使用了OnPush策略而父组件没有,子组件的@Input属性没有更改,但是ngOnCheck钩子执行的是当前组件变化的调用。

 

猜你喜欢

转载自blog.csdn.net/friend_ship/article/details/81742436
今日推荐