Angular 有效地使用 trackBy 和 ngFor

Posted

技术标签:

【中文标题】Angular 有效地使用 trackBy 和 ngFor【英文标题】:Angular efficiently using trackBy with ngFor 【发布时间】:2018-09-27 15:03:24 【问题描述】:

在 Angular 中,*ngFor 是否需要 trackBy 函数?我看到几篇文章here、here、here 和here 说使用trackBy 会提高性能并具有更好的内存管理。但我想知道如果 trackBy 是这样的改进,那为什么它不是默认行为呢?它是默认行为吗?我正在查看的所有内容都已过时?

如果它不是默认行为,我的项目在 90 个组件中大约有 90 个 *ngFor,我想知道是否有一种方法可以在我不包含以下函数 90 次的情况下使用 trackBy。我还想避免添加服务并导入 90 次。

HTML

<mat-option *ngFor="let l of list; trackBy: trackByFn" [value]="l.id">
   l.name
</mat-option>

TS

trackByFn(index, item) 
    return index

【问题讨论】:

【参考方案1】:

我创建了一个动画,展示了ngForngFortrackBy 如何并排操作DOM

在此处阅读文章:https://link.medium.com/ckBRk9wrinb

【讨论】:

【参考方案2】:

给它一个测试类

export class Item 
  id: number;
  name: string;

并添加一个指令来监控它的初始化和销毁​​

@Directive(selector: '[appMonitor]')
export class MonitorDirective implements OnInit, OnDestroy 


  ngOnInit(): void 
    console.log('init');
  

  ngOnDestroy(): void 
    console.log('destroy');
  

初始化数组

  itemArray: Item[] = [
    id: 1, name: 'Tom',
    id: 2, name: 'Joe',
    id: 3, name: 'KK'
  ];

改变数组内容的两个函数

  allFoo(): void 
    this.itemArray = [
      id: 1, name: 'Tom_foo',
      id: 2, name: 'Joe_foo',
      id: 3, name: 'KK_foo'
    ];
  

  allBar(): void 
    this.itemArray = [
      id: 1, name: 'Tom_bar',
      id: 2, name: 'Joe_bar',
      id: 3, name: 'KK_bar'
    ];
  

准备工作已经完成,到目前为止一切顺利。 首先让我们在没有 trackBy 的情况下进行测试

  <div *ngFor="let item of itemArray " appMonitor>
    Id: item.id Name:item.name
  </div>

很明显,每次更改数组角度时都会相应地重新创建组件。让我们尝试 trackBy 这一次:

<div *ngFor="let item of itemArray ;trackBy:identify" appMonitor>
  Id: item.id Name:item.name
</div>

身份:

  identify(index: number, item: Item): number 
    return item.id;
  

该组件正在重新使用。因此我们可以得出结论,使用trackBy可以节省在hmtl中创建相同组件的工作。

【讨论】:

【参考方案3】:

请注意,您的示例都没有使用索引(除了一篇不可靠的媒体文章),它们都使用对象的唯一标识符,除非您告诉它,否则 Angular 不可能知道。

单独的返回索引有一个用例,但它相当罕见。它基本上告诉 Angular 永远不要重新渲染此列表中的现有项目,因为给定项目的索引永远不会改变。这对于开发人员来说通常是一个非常意外的行为,因为子组件的初始化生命周期钩子不会重新执行。尽管在没有子组件的 ngFor 中,这通常是安全的,但这些类型的列表通常性能更高,除非列表很长或经常更改,否则您不会看到太多好处。

trackBy 的想法是允许您重新初始化列表中需要它的项目,而不是重新初始化不需要的项目。它不是像某些人对待它那样盲目地提高性能的灵丹妙药,它的目的和功能应该被充分理解。请记住,仅仅因为一个项目具有唯一的 ID 并不意味着它适合在 trackBy 函数中使用。 trackBy 旨在告诉 angular 何时需要重新渲染项目,即当我需要重新运行那些生命周期挂钩时。如果 ID 保持不变,但内容可能会发生变化,这取决于您构建某个组件的方式,那么该组件可能仍需要重新初始化。

【讨论】:

好的,假设我改用return item.id。由于它是唯一标识符,那么我应该看到一些内存/性能增加?如果是这样,是否有一种更简洁的方式来使用 trackBy 而无需在每个组件中创建函数? item.id 肯定会提高性能,因为 Angular 会知道它何时会并且不需要在您的 ngFor 中重新渲染项目,但如果项目没有那么多的 id 属性,显然会中断别。据我所知,没有设置 trackBy 的全局方法,也没有多大意义,因为在某些情况下需要覆盖它。您可以获得的最接近的是某种基础“ListComponent”,它实现了您自己设计的默认 trackByFn,但您仍然需要在模板中明确声明其用法。 我编辑了我的答案,以便更清晰/更详细地使用 trackBy 感谢您的回复,让我们假设我所有的对象都有 id,因为它们都来自一个数据库。我们有一个添加通用函数的组件,但我们需要将它添加到导入、提供程序和构造函数中才能在 html 中引用它,这与 return item.id 函数的行数相同。我只是想看看是否有一种有效的方法来添加它而不必添加这么多行复制/粘贴的代码 就像我说的那样,您可以创建一个基本列表组件类来实现该功能,并让任何带有 ngFor 的组件都继承自它。这可能与导入和构造的行数相同,但如果需要更改,更改将无限容易。如果你对 angular cli 足够好,你可以实际设置它,以便它可以为你搭建这个组件。

以上是关于Angular 有效地使用 trackBy 和 ngFor的主要内容,如果未能解决你的问题,请参考以下文章

Angular 9 Reactive Forms:使用 trackBy 时复选框未更新

typescript Angular - Ng For TrackBy for Performance

如何使用 `trackBy` 和 `ngFor`

如何有效地使用 Angular Material 自定义调色板颜色 Angular Material

如何在 Angular 2 beta 的服务中有效地使用 Http 组件?

trackBy 与异步管道