组件需要很长时间才能随机呈现列表
Component takes long time to render list randomly
我的某个组件出现异常行为。该组件从 ngOInit()
中的服务加载对象列表。
我面临的问题完全是随机的,有时需要很多时间来呈现这个列表,有时它是即时的。
我已经排除这是后端问题,对后端的调用总是很快。我发现了以下内容。添加钩子 DoCheck
后,我看到提到的问题发生时需要相当长的时间,因为 ngOnInit()
到达终点并调用 ngDoCheck()
。
根据 Angular 的文档 ngDoCheck()
在 ngOnIni()
之后调用,所以我不完全理解为什么有时需要这么长时间。我添加了 console.log
时间来计算每次调用的执行时间-
我在 ngOnInit()
的最后一行之后添加了日志,当获取对象列表的调用实际完成时并且在 ngDoCheck()
.
这是花费大量时间时浏览器控制台的样子:
如您所见,它已完成 ngOnInit()
,然后完成 ngDoCheck()
,完成获取项目,然后在 10 秒后运行 ngDoCheck()
,当列表实际呈现时在屏幕上。
这是带有 ngOnInit()
和 ngDoCheck()
的组件代码的一部分:
ngDoCheck(){
console.log('ngDoCheck. N. Items ' + this.items.length, new Date());
}
ngOnInit() {
this.isLoading = true;
this.sensorTypeConfigEndpoint.getInheritedType().then(result => {
this.typeInherited = result;
if (this._markers) {
const groups = this._markers.map(marker => marker.get('group'));
if (groups && groups.length) {
this.getItemsByMapVisualization(this.typeViewMap, _.cloneDeep(groups)).then( items => {
this.items = items;
console.log('items N. Items ' + this.items.length, new Date())
this.isLoading = false;
});
}
}
});
console.log('ngOnInit N. Items ' + this.items.length, new Date())
}
这是 HTML:
<div class="flex-cards" [ngStyle]="{'max-height': maxHeight}" [class.columnWrap]="isClusterInside" [appLoading]="isLoading">
<div (click)="expand()" *ngIf="isClusterInside" class="grid-expand">
<img class="filter-grey" src="assets/images/expand_cluster.svg" alt="Expand" style="width: 100%; height: 100%;margin-left: 4px">
<p class="text-expand" style="margin-bottom: 10px">
Expandir
</p>
</div>
<section class="card" style="height: calc(100% - 12px); overflow-y: auto;">
<div class="content-card">
<div *ngFor="let item of items">
{{item.text}}
</div>
</div>
</section>
</div>
知道为什么列表可能需要这么长时间才能呈现吗?它是随机发生的。
请注意,ngDoCheck
在可能影响组件的变更检测周期中运行。所以它在 ngOnInit
之后运行一次,然后被其他东西再次调用(也许是鼠标移动?悬停在什么东西上?点击?如果没有 stackblitz 再现你的问题很难确定)但它似乎不是被迫的在您对服务的异步调用中。如果未触发更改检测,则 DOM 将不会更新,因此您将看不到您的组件。您也没有向我们展示您正在使用的更改检测策略,并且您在模板中使用的一些属性也会影响可能导致问题的组件布局。
尝试使用官方文档中的 ChangeDetectorRef to manually force checking for changes after the isLoading = false
part of your code. This will help you figure out if that's the change detection issue or something else. If it starts working, I'd suggest reading more about change detection and angular lifecycle 来弄清楚如何解决这个问题 - 我不会比他们做得更好:)
sensorTypeConfigEndpoint
看起来像是“Angular 的世界之外的东西”。当它对 this.items
和 this.isLoading
进行一些更改时,Angular 没有意识到并且没有重绘视图。
就好像Angular在睡觉,因为它看不到任何变化。 ngDoCheck()
一种“唤醒”Angular 并强制它考虑更改并重绘视图,但唤醒是一种副作用,它只允许您人为触发重绘。
为了正确触发重绘,使用ngZone
:
import { Injectable, NgZone } from '@angular/core';
...
constructor(public zone: NgZone) { ... }
...
this.items = items;
this.isLoading = false;
this.zone.run(() => { // The magic line
console.log('items N. Items ' + this.items.length, new Date())
});
我的某个组件出现异常行为。该组件从 ngOInit()
中的服务加载对象列表。
我面临的问题完全是随机的,有时需要很多时间来呈现这个列表,有时它是即时的。
我已经排除这是后端问题,对后端的调用总是很快。我发现了以下内容。添加钩子 DoCheck
后,我看到提到的问题发生时需要相当长的时间,因为 ngOnInit()
到达终点并调用 ngDoCheck()
。
根据 Angular 的文档 ngDoCheck()
在 ngOnIni()
之后调用,所以我不完全理解为什么有时需要这么长时间。我添加了 console.log
时间来计算每次调用的执行时间-
我在 ngOnInit()
的最后一行之后添加了日志,当获取对象列表的调用实际完成时并且在 ngDoCheck()
.
这是花费大量时间时浏览器控制台的样子:
如您所见,它已完成 ngOnInit()
,然后完成 ngDoCheck()
,完成获取项目,然后在 10 秒后运行 ngDoCheck()
,当列表实际呈现时在屏幕上。
这是带有 ngOnInit()
和 ngDoCheck()
的组件代码的一部分:
ngDoCheck(){
console.log('ngDoCheck. N. Items ' + this.items.length, new Date());
}
ngOnInit() {
this.isLoading = true;
this.sensorTypeConfigEndpoint.getInheritedType().then(result => {
this.typeInherited = result;
if (this._markers) {
const groups = this._markers.map(marker => marker.get('group'));
if (groups && groups.length) {
this.getItemsByMapVisualization(this.typeViewMap, _.cloneDeep(groups)).then( items => {
this.items = items;
console.log('items N. Items ' + this.items.length, new Date())
this.isLoading = false;
});
}
}
});
console.log('ngOnInit N. Items ' + this.items.length, new Date())
}
这是 HTML:
<div class="flex-cards" [ngStyle]="{'max-height': maxHeight}" [class.columnWrap]="isClusterInside" [appLoading]="isLoading">
<div (click)="expand()" *ngIf="isClusterInside" class="grid-expand">
<img class="filter-grey" src="assets/images/expand_cluster.svg" alt="Expand" style="width: 100%; height: 100%;margin-left: 4px">
<p class="text-expand" style="margin-bottom: 10px">
Expandir
</p>
</div>
<section class="card" style="height: calc(100% - 12px); overflow-y: auto;">
<div class="content-card">
<div *ngFor="let item of items">
{{item.text}}
</div>
</div>
</section>
</div>
知道为什么列表可能需要这么长时间才能呈现吗?它是随机发生的。
请注意,ngDoCheck
在可能影响组件的变更检测周期中运行。所以它在 ngOnInit
之后运行一次,然后被其他东西再次调用(也许是鼠标移动?悬停在什么东西上?点击?如果没有 stackblitz 再现你的问题很难确定)但它似乎不是被迫的在您对服务的异步调用中。如果未触发更改检测,则 DOM 将不会更新,因此您将看不到您的组件。您也没有向我们展示您正在使用的更改检测策略,并且您在模板中使用的一些属性也会影响可能导致问题的组件布局。
尝试使用官方文档中的 ChangeDetectorRef to manually force checking for changes after the isLoading = false
part of your code. This will help you figure out if that's the change detection issue or something else. If it starts working, I'd suggest reading more about change detection and angular lifecycle 来弄清楚如何解决这个问题 - 我不会比他们做得更好:)
sensorTypeConfigEndpoint
看起来像是“Angular 的世界之外的东西”。当它对 this.items
和 this.isLoading
进行一些更改时,Angular 没有意识到并且没有重绘视图。
就好像Angular在睡觉,因为它看不到任何变化。 ngDoCheck()
一种“唤醒”Angular 并强制它考虑更改并重绘视图,但唤醒是一种副作用,它只允许您人为触发重绘。
为了正确触发重绘,使用ngZone
:
import { Injectable, NgZone } from '@angular/core';
...
constructor(public zone: NgZone) { ... }
...
this.items = items;
this.isLoading = false;
this.zone.run(() => { // The magic line
console.log('items N. Items ' + this.items.length, new Date())
});