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

本文深入探讨Angular中的脏检查机制,解释其如何实现双向数据绑定和MVVM模式,包括$apply,$watch及$digest函数的作用,变更检测流程,以及OnPush策略的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“脏检查”是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钩子执行的是当前组件变化的调用。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值