英文:
Really slow change detection in hybrid Angular app
问题
我们有一个混合使用AngularJS和Angular 8的应用程序,经常遇到不同版本框架之间的变更检测非常慢的问题。到目前为止,我们只在使用AngularJS组件内部的Angular组件时遇到了这个问题。常见情况是在新的Angular 8表单中包含一些旧的AngularJS组件,对于一些特定的AngularJS组件,组件中的更改传播到Angular表单并进行表单验证可能需要2到10秒左右的时间。UI没有卡住或出现问题,它保持响应性,但不同版本框架之间的通信似乎存在不同程度的延迟。在同一表单中的其他AngularJS组件可能能够无缝工作。我们尚未找到根本原因,但我们找到了一个可靠的解决方法:在AngularJS组件中使用$timeout(() => $scope.apply());
,通常是在$onChanges
监听器中。这将触发Angular中的变更检测并消除延迟。
现在我们面临的问题是,有一个Angular组件用在了一个AngularJS组件内部。我们有一个新的Angular组件,本质上包装了一个NG Bootstrap日期选择器,在日期选择器中选择日期后,父级AngularJS表单在意识到已经进行了更改之前会有几秒钟不等的延迟(最多约10秒)。Angular的升级指南提到,在使用downgradeModule()
时,可以使用NgZone.run()
手动触发变更检测(我们正在使用),但我们还没有找到使其工作的方法。文档也没有详细介绍如何使用run()
。我们还尝试了ChangeDetectorRef.detectChanges()
,但没有成功。
我尝试将AngularJS父组件的$scope
传递给Angular组件,并在Angular组件中使用setTimeout(() => $scope.$apply());
在使用EventEmitter
发出更改事件后,这样做确实起作用,延迟消失了。但显然,这不是我们想要在实际项目中使用的方法。
有关如何消除AngularJS / Angular 8组件之间的变更检测延迟的建议,无论是通过从Angular组件手动触发变更检测还是其他方式?
英文:
We have a hybrid AngularJS / Angular 8 app, and we keep constantly running into issues with really slow change detection between components from different versions of the framework. Until now we've only had this problem when using AngularJS components inside Angular components. The common case is having a new Angular 8 form that has some old AngularJS components in it, and with some specific AngularJS components it can take anywhere between ~2 to ~10 seconds for the changes made in the component to propagate to the Angular form and for form validations to run. The UI doesn't hang or anything, it stays responsive, but there just seems to be a varying delay in communications between the different versions of the framework. Other AngularJS components in the same form might work seamlessly. We haven't found out the root cause for it yet, but we have found one reliable workaround; use $timeout(() => $scope.apply());
in the AngularJS component, usually in the $onChanges
-listener. This triggers change detection in Angular and removes the delay.
Now we're facing it with an Angular component that is used inside an AngularJS component. We have a new Angular component that essentially wraps an NG Bootstrap datepicker, and after selecting a date in the datepicker, there is a varying delay of several seconds (up to around 10 seconds) before the parent AngularJS form realizes that any changes were made. Angular's upgrade guide mentions that you can use NgZone.run()
to manually trigger change detection when using downgradeModule()
(which we are), but we haven't found a way to get it working. The documentation doesn't really go into detail how run()
should be used, either. We also tried ChangeDetectorRef.detectChanges()
, to no avail.
I tried passing the AngularJS parent component's $scope
down to the Angular component and using setTimeout(() => $scope.$apply());
in the Angular component after emitting a change event with an EventEmitter
, and that did work; the delay was gone. But obviously this is not something we want to use for real.
Any suggestions on how to eliminate the change detection delay between AngularJS / Angular 8 components, either by manually triggering change detection from an Angular component or some other way?
答案1
得分: 2
最终,我们没有找到除问题中描述的解决方案之外的其他方法:将AngularJS的$scope
作为输入参数传递给Angular组件,并在$scope.$apply()
中包装事件发射,即
$scope.$apply(() => valueChange.emit(value));
根据Angular升级指南,这似乎是在使用downgradeModule()
时使其正常工作的唯一方法。
英文:
In the end we didn't find any other solution than the one described in the question: passing the AngularJS $scope
as an input parameter to the Angular component and wrapping the event emissions in $scope.$apply()
, i.e.
$scope.$apply(() => valueChange.emit(value));
Based on the Angular upgrade guide, this seems to be the only way to get it working when using downgradeModule()
.
答案2
得分: 0
在AngularJS运行周期($digest)中会出现问题。这样,AngularJS可以评估模型和视图之间的更改。
在每个$digest周期中,会执行监视器(watchers)。这是Angular评估附加到视图的表达式并将其重新呈现给用户的阶段。
有很多时候,客户端中的操作应该在“Angular世界”之外执行,这意味着Angular不知道这些更改并且不将更改反映给用户。有几种方法可以解决这个问题,其中一种如您所提到的使用$timeout,方法包括:
- $apply()
- $timeout()
- $digest()
- $evalAsync()
$evalAsync()首次引入于AngularJS 1.2.X,对我来说是最好的方法。
在引入$evalAsync()之前,Angular团队正式回答说,当您在周期中遇到问题并希望从“Angular世界”之外反映更改时,使用$timeout()。
在Angular进化并且更多用户经历了这个已知问题之后,Angular团队创建了$evalAsync()。此函数将在当前周期或下一个周期中评估表达式。
更多详情请查看:source
英文:
Problems occur in AngularJS as it runs in cycles ($digset). That way, AngularJS can evaluate the changes between the model and the view.
In every $digest cycle, the watchers are executed. This is the phase where Angular evaluates the expressions that are attached to the view and re-renders them back to the user.
There are a lot of times when operations in the client should and need to be done outside of the “Angular world”, which means Angular is not aware of these changes and does not reflect the changes to the user. There are several methods to resolve this issue, one as you mentioned using $timeout, methods are:
- $apply()
- $timeout()
- $digest()
- $evalAsync()
$evalAsync() was first introduced in AngularJS 1.2.X, and for me, it’s the best method to use.
Before $evalAsync() was introduced, Officially answered by the Angular team, when you have issues with cycles and want to reflect changes from outside the “Angular world”, use $timeout().
After Angular has evolved and more users have experienced this known issue, the Angular team has created the $evalAsync(). This function will evaluate the expression during the current cycle or the next.
For more details: source
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论