Angular 即使没有做任何更改也会无限重新渲染是否正常?
Is it normal for Angular to re-render infinitely even when no changes are made?
问题
Angular 是否旨在不断重新检查所有内容以检测变化?我来自 React 世界,我期待像 event triggered -> re-rendering
这样的东西。我不知道这是来自我的应用还是来自 Angular.
如果我有一个从 HTML 模板调用的方法,即使我的组件没有任何变化,它也会被无限调用。
问题
我有一个类似日历的页面,它加载了大量数据并且必须在呈现之前进行计算,因为使用 ngIf
指令执行这些操作太困难了。所以我的模板中有一些东西看起来像:
<div [innerHTML]="_getDayPrice(item, day) | safeHtml"></div>
如果我在 _getDayPrice
方法中控制台记录一些东西,它会被无限打印。
我试过的
我通过在我的应用程序中手动注入 ChangeDetectionRef
并执行 this.cdRef.detach()
设法绕过了这个问题。然而,这感觉很糟糕,因为有时我可能需要重新启用它并再次分离。
我试图调查它是否来自我的父组件应用程序,例如容器。我在我的主 app.component 中呈现了一个 div 就像 <div class={{computeClass()}}>
并且在那个方法中打印了一个控制台日志并且果然它被无限调用。因此,在此之后,我尝试注释掉所有应用程序的服务。如果所有内容都被注释掉,它确实可以正常工作,但也没有可观察到的数据。我已经调查了大约半天,但找不到单点故障(比如注释掉此服务可以解决所有问题)。
使用 chrome 的内置性能选项卡记录性能,但再次无法从我的代码中找到任何触发更改的内容。 zone.js
被反复调用并且似乎设置了一个持续触发的间隔。
当然,我已经在服务中搜索了 setTimeout
和 setInterval
的出现,但找不到可能导致此问题的不断变化的内容。
结论?
底线是:如果您有一个复杂的 Angular 应用程序并从模板调用一个方法,该方法将被无限调用,这是否正常?
如果不是,您是否有任何关于可能导致此问题的提示?或者任何其他绕过它而不是分离 changeRef 检测器的方法?
我唯一关心的是性能。这是一个类似日历的页面,呈现多行,并且在 8GB RAM 笔记本电脑上严重滞后。我很确定平板电脑或 phone 几乎会死机。
有两种机制可以减少绑定检查的数量
1 - ChangeDetectorRef.detach:允许将您的组件从更改检测循环中分离出来,因此在重新连接之前不会刷新出价。
2 - ChangeDetectionStategy.OnPush : Tell Angular that the bindings of your component need to be checked only when at least one of the component @Input has changed. Your can find me details on how to use it here
IMO,您应该创建一个包含 item / days 输入的组件来包装您的显示 DOM 元素。像
// wrap.component.ts
@Component({
template: '<div [innerHTML]="_getDayPrice() | safeHtml"></div>',
changeDetection: ChangeDetectionStrategy.OnPush,
...
})
export class WrapComponent {
@Input() item: Item;
@Input() day: String;
private _getDayPrice = () => {
// compute your HTML with this.item and this.day instead of parameters
....
}
}
那么您应该会看到仅当项目或日期更改时触发的逻辑。
在 HTML.
中转换数据时应尽可能多地使用管道
管道仅在管道对象或参数更改时重新评估(因此,如果输入之一是对象,请确保创建该对象的新实例以触发重新评估)。对于大多数用途,您可以使用管道而不是函数。
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'getDayPrice'
})
export class GetDayPricePipe implements PipeTransform {
transform(item: string, day: string): string {
// ... do whatever logic here
return item + day;
}
}
然后像这样使用它:
<div [innerHTML]="item | getDayPrice:day | safeHtml"></div>
问题
Angular 是否旨在不断重新检查所有内容以检测变化?我来自 React 世界,我期待像 event triggered -> re-rendering
这样的东西。我不知道这是来自我的应用还是来自 Angular.
如果我有一个从 HTML 模板调用的方法,即使我的组件没有任何变化,它也会被无限调用。
问题
我有一个类似日历的页面,它加载了大量数据并且必须在呈现之前进行计算,因为使用 ngIf
指令执行这些操作太困难了。所以我的模板中有一些东西看起来像:
<div [innerHTML]="_getDayPrice(item, day) | safeHtml"></div>
如果我在 _getDayPrice
方法中控制台记录一些东西,它会被无限打印。
我试过的
我通过在我的应用程序中手动注入
ChangeDetectionRef
并执行this.cdRef.detach()
设法绕过了这个问题。然而,这感觉很糟糕,因为有时我可能需要重新启用它并再次分离。我试图调查它是否来自我的父组件应用程序,例如容器。我在我的主 app.component 中呈现了一个 div 就像
<div class={{computeClass()}}>
并且在那个方法中打印了一个控制台日志并且果然它被无限调用。因此,在此之后,我尝试注释掉所有应用程序的服务。如果所有内容都被注释掉,它确实可以正常工作,但也没有可观察到的数据。我已经调查了大约半天,但找不到单点故障(比如注释掉此服务可以解决所有问题)。使用 chrome 的内置性能选项卡记录性能,但再次无法从我的代码中找到任何触发更改的内容。
zone.js
被反复调用并且似乎设置了一个持续触发的间隔。当然,我已经在服务中搜索了
setTimeout
和setInterval
的出现,但找不到可能导致此问题的不断变化的内容。
结论?
底线是:如果您有一个复杂的 Angular 应用程序并从模板调用一个方法,该方法将被无限调用,这是否正常?
如果不是,您是否有任何关于可能导致此问题的提示?或者任何其他绕过它而不是分离 changeRef 检测器的方法?
我唯一关心的是性能。这是一个类似日历的页面,呈现多行,并且在 8GB RAM 笔记本电脑上严重滞后。我很确定平板电脑或 phone 几乎会死机。
有两种机制可以减少绑定检查的数量
1 - ChangeDetectorRef.detach:允许将您的组件从更改检测循环中分离出来,因此在重新连接之前不会刷新出价。
2 - ChangeDetectionStategy.OnPush : Tell Angular that the bindings of your component need to be checked only when at least one of the component @Input has changed. Your can find me details on how to use it here
IMO,您应该创建一个包含 item / days 输入的组件来包装您的显示 DOM 元素。像
// wrap.component.ts
@Component({
template: '<div [innerHTML]="_getDayPrice() | safeHtml"></div>',
changeDetection: ChangeDetectionStrategy.OnPush,
...
})
export class WrapComponent {
@Input() item: Item;
@Input() day: String;
private _getDayPrice = () => {
// compute your HTML with this.item and this.day instead of parameters
....
}
}
那么您应该会看到仅当项目或日期更改时触发的逻辑。
在 HTML.
中转换数据时应尽可能多地使用管道管道仅在管道对象或参数更改时重新评估(因此,如果输入之一是对象,请确保创建该对象的新实例以触发重新评估)。对于大多数用途,您可以使用管道而不是函数。
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'getDayPrice'
})
export class GetDayPricePipe implements PipeTransform {
transform(item: string, day: string): string {
// ... do whatever logic here
return item + day;
}
}
然后像这样使用它:
<div [innerHTML]="item | getDayPrice:day | safeHtml"></div>