请向我解释为什么我一直得到这个错误:ExpressionChangedAfterItHasBeenCheckedError:表达式已经改变后,它被检查。
显然,我只有在开发模式下才会遇到这种情况,在我的产品构建中不会出现这种情况,但这非常烦人,而且我根本不明白在我的开发环境中出现错误而不会在prod上显示的好处——可能是因为我缺乏理解。
通常,修复很简单,我只是把导致错误的代码包装在setTimeout中,就像这样:
setTimeout(()=> {
this.isLoading = true;
}, 0);
或者使用如下构造函数强制检测更改:
this.isLoading = true;
this.cd.detectChanges();
但是为什么我总是遇到这个错误呢?我想要了解它,这样我就可以在将来避免这些俗套的修复。
参考文章https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
因此,变更检测背后的机制实际上是以同步执行变更检测和验证摘要的方式工作的。这意味着,如果我们异步更新属性,当验证循环运行时,值不会被更新,我们也不会得到ExpressionChanged…错误。我们得到这个错误的原因是,在验证过程中,Angular看到的值与它在变更检测阶段记录的值不同。所以为了避免....
1)使用changeDetectorRef
2)使用setTimeOut。这将在另一个VM中作为宏任务执行您的代码。Angular在验证过程中不会看到这些更改,你也不会得到这个错误。
setTimeout(() => {
this.isLoading = true;
});
3)如果你真的想在相同的虚拟机上执行你的代码使用
Promise.resolve(null).then(() => this.isLoading = true);
这将创建一个微任务。微任务队列是在当前同步代码完成执行之后处理的,因此对属性的更新将在验证步骤之后发生。
尝试了上面建议的大部分解决方案。只有在这种情况下对我有用。我使用*ngIf来切换角材料的不确定渐进条基于api调用,它抛出ExpressionChangedAfterItHasBeenCheckedError。
在上述组件中:
constructor(
private ngZone: NgZone,
private changeDetectorRef: ChangeDetectorRef,
) {}
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
this.appService.appLoader$.subscribe(value => {
this.loading = value;
this.changeDetectorRef.detectChanges();
});
});
}
诀窍是使用ngzone绕过角分量的变化检测。
PS:不确定这是否是一个优雅的解决方案,但是使用AfterContentChecked和AfterViewChecked生命周期钩子必然会引起性能问题,因为你的应用程序会越来越大,因为它会被多次触发。