【问题标题】:Component takes long time to render list randomly组件需要很长时间才能随机呈现列表
【发布时间】:2021-07-12 07:16:56
【问题描述】:

我的一个组件出现了奇怪的行为。该组件从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>

知道为什么列表可能需要这么长时间才能呈现吗?它是随机发生的。

【问题讨论】:

  • 据我了解,'items N. Items ' 是您最后的日志。当一切都完成时,它就来了。但是这个日志来得很快。在控制台中,ngOnInit 在 08:56:23 记录,最后一个日志 'items N. Items ' 在同一秒内到达... 08:56:23。然后你有一堆ngDoCheck()的日志,但这没什么,所有的核心逻辑都已经做完了。我不确定我是否理解问题出在哪里
  • 问题是当所有的逻辑都完成后,HTML 还没有渲染列表。当第二个ngDoCheck() 进来时,它实际上会这样做。

标签: javascript angular typescript typescript2.0


【解决方案1】:

sensorTypeConfigEndpoint 看起来像是“在 Angular 的世界之外”。当它对this.items 和this.isLoading 进行一些更改时,Angular 没有意识到这一点并且不会重绘视图。

就像 Angular 正在睡觉,因为它看不到任何变化发生。 ngDoCheck() 一种“唤醒” Angular up 并强制它考虑变化并重绘视图,但唤醒是副作用,它只允许您人为地触发重绘。

为了正确触发重绘,请使用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())
});

【讨论】:

  • 我仍然必须找到“在 Angular 的世界之外”的调用,但我已经尝试过这个解决方案并且效果很好。谢谢!!
【解决方案2】:

请注意,ngDoCheck 在可能影响组件的更改检测周期上运行。所以它在ngOnInit 之后运行一次,然后被其他东西再次调用(可能是鼠标移动?悬停在某物上?点击?如果没有堆栈闪电战重现你的问题,很难确定),但它似乎不是从内部强制您对服务的异步调用。如果未触发更改检测,则 DOM 将不会更新,因此您将看不到您的组件。您也没有向我们展示您正在使用什么更改检测策略,并且您在模板中使用的一些属性也会影响可能导致问题的组件布局。

尝试使用ChangeDetectorRef 在代码的isLoading = false 部分之后手动强制检查更改。这将帮助您确定这是更改检测问题还是其他问题。如果它开始工作,我建议在官方文档中阅读更多关于 change detection 和 angular lifecycle 的信息,以了解如何解决这个问题 - 我不会比他们做得更好:)

【讨论】:

  • 检测策略是默认的。我在isLoading = false 之后添加了checkNoChanges(),它会在检测到更改时抛出异常。模板中的属性没有影响,因为我还尝试仅将 HTML 与列表的一段代码一起保留,仅此而已,问题仍然存在。
猜你喜欢
  • 2012-12-04
  • 2019-09-12
  • 1970-01-01
  • 2013-01-22
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
相关资源
最近更新 更多