使用方法调用而不是 ngFor 指令的属性时无休止的负载

Posted

技术标签:

【中文标题】使用方法调用而不是 ngFor 指令的属性时无休止的负载【英文标题】:Endless load when using method call instead of property for ngFor directive 【发布时间】:2018-08-12 06:58:34 【问题描述】:

在我的模板中,我有以下代码:

<a *ngFor="let item of navItems"
   [routerLink]="item.link"
   routerLinkActive="active"
   class="navigation-item"
   [ngClass]="'enabled': item.enabled"
>
  <span class="color-item.color">
    <mat-icon>item.icon</mat-icon>
  </span>
  <div class="label">item.label</div>
</a>

navItems 是我组件中的一个数组:

navItems: NavItem[] = [
  
    link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
  ,
  
    link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
  ,
  
    link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
  ,
  
    link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
  ,
];

这很好用。现在,我需要更改它,以便 navItems 可以动态更改。我尝试将navItems 属性转换为像这样的getter:

get navItems(): NavItem[] 
  return [
    
      link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
    ,
    
      link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
    ,
    
      link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
    ,
    
      link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
    ,
  ];

然而,一旦我这样做,浏览器选项卡就会在加载组件时陷入无休止的加载循环,并且必须通过任务管理器终止 - 我没有得到控制台输出,什么也没有。

我也试过用普通的方法调用而不是getter来提供数组,结果一样

我返回的数组仅由具有直接分配的字符串和布尔文字的普通对象组成,因此那里不会发生进一步的调用,因此不太可能只是一个递归循环被我忽略了。

我做错了吗?你不能使用方法调用/getter 来提供 ngFor 指令的项目吗?

【问题讨论】:

是的,您可以使用 getter 或方法调用来为ngFor 循环提供项目,如this stackblitz 所示。必须有其他事情发生;您将不得不向我们展示更多您的代码。 @ConnorsFan 感谢确认这通常可以工作 - 我已经为问题添加了更多代码,这是否提供了更多见解? 您是否还在代码的其他部分使用navItems?例如,在life cycle hooks(例如ngAfterViewInit)中。 @ConnorsFan 不,我没有,这是迄今为止唯一的用法。 它是否需要成为 getter 或方法?如果您修改原始集合,UI 不会更新以反映更改吗? 【参考方案1】:

在这里,当我使用时,trackBy 然后它解决了问题

html文件中

<a *ngFor="let item of navItems; trackBy: trackByFn" [routerLink]="item.link" routerLinkActive="active"
class="navigation-item"
[ngClass]="'enabled': item.enabled"
>
<span class="color-item.color">
 <mat-icon>item.icon</mat-icon>
</span>
<div class="label">item.label</div>
</a>

在ts文件中

export class AppComponent 
   trackByFn(index,item)
     console.log(index,"n:",item);
     return index;
   
  get navItems(): NavItem[] 
    return [
      
        link: 'link1', label: 'Label 1', icon: 'thumb_up', color: 'green', enabled: true
      ,
      
        link: 'link2', label: 'Label 2', icon: 'thumb_down', color: 'red', enabled: true
      ,
      
        link: 'link3', label: 'Label 3', icon: 'insert_drive_file', color: 'blue', enabled: true
      ,
      
        link: 'link4', label: 'Label 4', icon: 'note_add', color: 'blue', enabled: true
      ,
    ];
  

class NavItem 
  link;
   label;
    icon;
     color;
     enabled;

注意:

case-1 : 没有 routerlinkactive[rla] 指令和 trackBy 工作正常

case-2 : 使用 rla 和 trackBy 可以正常工作

case-3 : 带 rla 且不带 trackBy,--> 循环

在这里,RouterLinkActive 指令实现 [AfterContentInit,并且 其他] 在这个 AfterContentInit 里面有一个方法调用 更新()...负责这个连续循环..我 相信,当方法返回数组时..首先是元素 将被创建,然后指令开始对其进行操作.. [仅供参考: 当 update() 被注释掉并且没有 trackBy 时,这也有效 很好,因为它不能执行钩子方法。]

如果您观察到方法调用 navItems ,方法调用数 = 数组中的项目数。 每次返回数组时,它都会渲染这些值,并且指令将作用于现有元素,并且指令具有钩子[AfterContentInit,onDestroy],它们负责添加元素类并销毁。

因此在没有 trackBy 的情况下,在每个方法 return 上,元素将 被创建并且指令将通过附加钩子来作用于它 在下一个方法调用中,它们将被视为 new ,并且 指令会做同样的事情,但会破坏之前的元素。 这样它就会进入循环......

希望对你有帮助!!!

参考:https://netbasal.com/angular-2-improve-performance-with-trackby-cc147b5104e5

参考:使用来自https://github.com/angular/angular 的源代码创建 CustomRouterLinkActive 进行调试

【讨论】:

因为 trackBy 有助于呈现新值而不是构建整个值数组。示例:即使您传递新数组(与旧数组具有相同的值)...如果我们不使用trackBy 然后整个值再次呈现..【参考方案2】:

我认为这里的问题是,当您将数组放在实例变量中时,数组总是相同的。我的意思是,每次 Angular 尝试检测更改时,它都会检测到数组是相同的,因此它没有执行进一步的操作。

但是,一旦将实例变量更改为 getter,每次调用 navItems 时都会返回一个新数组。在这种情况下,每次 Angular 进行更改检测时,它都会找到不同的值。从概念上讲,这些项目是相同的,这并不重要。数组是不同的对象。

我的意思是,之前:

// if comp is an instance of your component:
const a = comp.navItems;
const b = comp.navItems;
console.log(a === b); // true. a and b are pointers to the same object.

但是在你改变之后:

const a = comp.navItems; // assigning a the return value of a func!
const b = comp.navItems: // assigninb b the return of a NEW call of a func
console.log(a === b); // false. Two arrays were created here. Two objects. 

为避免此问题,您应该按照上面的建议使用 trackByFunction。这样,只有新的/修改的项目会再次被渲染。您也不应该每次都返回一个新数组,只有在数据真正发生变化时才返回。如果您仍想在 getter 中动态地构造数组,请考虑在组件中使用 ChangeDetectionStrategy.OnPush,然后使用 ChangeDetectorRef.markForCheck() 指示组件数据已更改,并且应在更改检测期间对其进行检查。如果你不这样做,Angular 将永远不会重新渲染组件,除非它的任何 Input 属性发生变化。

【讨论】:

以上是关于使用方法调用而不是 ngFor 指令的属性时无休止的负载的主要内容,如果未能解决你的问题,请参考以下文章

Angular:如何使用 *ngFor 将指令绑定到元素

ngFor 指令是不是会在每个突变上重新渲染整个数组?

自动列出当前 Object 数据迭代的每个属性值的动态表组件(ngFor 指令);

用另一个结构指令包装 Angular ngFor 指令

为啥不能在 ngFor 中使用 var 而不是 let

Angular 2 - NgFor 使用数字而不是集合