Angular中 detecChanges 和 markForCheck的区别
Angular 中 ChangeDetectorRef类:
1.markForCheck方法,顾名思义,是给这个Component做标记,以便后续变更检测的时候知道这个组件的中有变更也要进行检测,所以它本身并不触发变更检测。那么为什么要这样做呢,直接给他全都变更检测不就行了吗? Actually,过多的变更检测会影响整个应用的性能,只在必要的时候进行更能提高效率。Angular默认的变更检测机制是 ChangeDetectionStrategy.Default,也就是触发整个组件树,从根组件开始,自上而下地,对每个组件都进行变更检测。因此,如果我们要用 m 方法,使用默认的变更检测策略就不太合适了。 这时候我们搭配使用的变更检测机制是 ChangeDetectionStrategy.OnPush。
OnPush的原理是什么呢,借用一个youTuber的视频截图来看看(详情可观看视频 What's the difference between markForCheck() and detectChanges())。在这张图中,给Cmp2加了 ChangeDetectionStrategy.OnPush机制,所以能看到在点击左上角的触发变更检测的按钮后,Cpm2本身以及它的子组件并没有进行变更检测,而组件树上的其他组件都无一例外地自上而下全部进行了变更检测。
了解了 OnPush的原理后,我们再调用 markForCheck 方法的效果就呼之欲出了。标记这个组件,再次做变更检测的时候,其组件及子组件都会参与检测,完成后重新绘制视图。
什么时候用到OnPush和markForCheck呢,举个例子
前提:在子组件中为了提高应用的效率使用了 ChangeDetectionStrategy.OnPush机制。当我们在子组件中定义了一个对象类型的接口的时候,如果父组件赋予了属性初值后,又想改变其中一个或几个属性的值,并让子组件检测到这个更新,就需要在子组件中加上markForCheck方法。
原因是!使用OnPush机制时,改变 @Input的引用就会触发变更检测,而对象类型是个例外。
因为除了 Object 以外的所有类型(即原始类型,包括Boolean、Null、Undefined、Number、String、Symbol)都是不可变的(Immutable),是通过值传递的,每次对它们的改动都会在内存里生成一个新的值。而 Object 是通过引用传递的,每次对 Object 改动,引用不会改变。(引用自Angular Change Detection:变化检测策略 )也就是说,当使用了OnPush之后,即使我们改变了对象里一个属性的值,但因为对象的引用没有改变,所以不会触发变更检测。为了变更检测的时候不会略过这个组件,这时候就需要加上markForCheck方法了。
2.detectChanges方法:顾名思义,“变化检测”。当我们的model内部发生改变但没有反映到视图上的时候,需要通知Angular来检测这些变化并更新视图,也就是说 调用 detectChanges 会 触发变更检测!
以下有三种情况属于class内部发生变化但没有立刻反映到视图上的:
(1) 使用了 detach 方法(个人认为可以翻译为隔离;不抓取),detach一个视图之后,即使这部分被标记为脏数据了,但是仍然不会被变更检测到,直到使用 reattch 方法或 detectChanges方法。
(2)NgZone可以理解为一个异步事件拦截器,主要负责通知Angular触发变更检测并更新页面。但是如果已经发生了更新,但它没有在 Angular 区域内,因此NgZone捕获不到更新就无法通知Angular,调用detectChanges方法就可以触发变更检测并更新了。
比如在一个单独的函数中更新了model中的内容:
someFunctionThatIsRunByAThirdPartyCode(){
yourModel.text = "new text";
}
myFunction(){
someFunctionThatIsRunByAThirdPartyCode();
// Let's detect the changes that above function made to the model which Angular is not aware of.
this.cd.detectChanges();
}
(代码引用自# What's the difference between markForCheck() and detectChanges())
(3)第三种情况就是在组件生命周期完成后对model进行了变更,如果不使用 detectChanges, 就会出现这样的报错:
"Expression has changed after it was checked";
解决这个报错的方法有三个
方法一:使变更操作在生命周期内进行;
方法二:调用 detectChanges;
方法三:使用 setTimeOut函数,因为 setTimeOut 函数做完异步操作后会自动进行变更检测。