使用未定义副作用的角度轨迹函数

XXI*_*XIV 1 typescript ngfor angular

我的一个组件中存在更改检测运行过于频繁的问题,因此我尝试使用该指令trackBy的选项ngFor

通过阅读,我了解到 Angular 将在下次运行更改检测时使用从函数返回的值作为trackyBydiff。为了看看它是否符合我的需求,并尝试更好地理解它,我建立了一个游乐场。使用的时候我把trackyBy我用的函数的返回值设置为return undefined,结果还是得到了我想要的结果。

TS:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  styleUrls: ['./app.component.scss'],
  templateUrl: './app.component.html',
})
export class AppComponent {
collection;
  constructor() {
    this.collection = [{id: 1, value: 0}, {id: 2, value: 0}, {id: 3, value: 0}];
  }

  getItems() {
    this.collection = this.getItemsFromServer();
  }

  getItemsFromServer() {
    return [{id: 5, value: 0}, {id: 2, value: 0}, {id: 3, value: 3}, {id: 4, value: 4}];
  }

  trackByFn(index, item) {
    return undefined;
  }
}
Run Code Online (Sandbox Code Playgroud)

HTML:

    <ul>
      <li *ngFor="let item of collection;trackBy: trackByFn">{{ item.id }}hello {{item.value}}</li>
    </ul>
    <button (click)="getItems()">Refresh items</button>
Run Code Online (Sandbox Code Playgroud)

第一次单击的结果是,除了数组的索引 1 之外,所有项目都以其新值或 id 重新呈现。第二次单击时,没有任何项目重新渲染,因为对象内没有任何变化。

所以我的问题是,为什么要使用唯一的 id 作为trackBy函数的返回值?一定有一些我遗漏的东西,我不希望它以我还没有看到的方式影响我的申请。

Kur*_*ton 5

官方的答案是,您可以避免trackBy在 DOM 中为具有相同标识符的对象的新实例重新创建元素。

从表面上看,您的设置并不能证明当模型中出现新实例时,您的设置ngFor并不会忽略undefined您返回的值并重新创建 DOM 元素。trackByFn

与现有对象具有相同 id 的新项目可能已更改其他属性,因此无论您是否使用(或误用),您都希望 HTML 是正确的trackBy

设置

我使用您的代码创建了一个测试环境,但我分叉了源代码,*ngFor以便我可以添加自己的日志记录来跟踪*ngFor.

我测试了三种场景:

A)trackByFn返回唯一的id

B)trackByFn回报undefined

C) 不使用trackBy

我按照以下步骤跟踪了每个场景中发生的情况

  1. 创建列表
  2. 部分替换部分列表数据
  3. 排序列表
  4. 重置列表数据

我在每个步骤中分配了新的对象实例以进行“纯粹”测试。

结果

1.创建列表

所有 3 个场景都是相同的 - 为列表中的每个项目创建一个 DOM 元素。

2.部分替换部分列表数据

A) 删除已删除项目的 DOM 元素并为新项目创建新的 DOM 元素。所有元素均已更新。

B) 为索引超出原始数组范围的项创建新的 DOM 元素。所有元素均已更新。

C) 重新创建所有 DOM 元素。

3.列表排序

A) 移动已移动位置的 DOM 元素

B) 更新已移动位置的 DOM 元素

C) 重新创建所有 DOM 元素

4.重置列表数据

A) 删除已删除项目的 DOM 元素并为新项目创建新的 DOM 元素。所有元素均已更新(与场景 2 相同)。

B) 删除已删除项目的 DOM 元素。所有元素均已更新。

C) 重新创建所有 DOM 元素。

结论

值得注意的是,这些测试是使用新的对象实例完成的。*ngFor如果重用对象引用,效率会更高。

trackBy如果您有一个非常不稳定的列表,那么在 DOM 操作方面使用会更有效。

令人惊讶的结果

从我的测试来看,您的示例执行的 DOM 操作比从trackByFn. 如果您用 3 个新项目替换 3 个项目,您的方法不会执行任何 DOM 操作,并且仍会运行与“正确”方式相同的更新方法。“正确的”方法会删除原来的 3 个 DOM 元素并添加 3 个新的 DOM 元素。

这表明我们可以只提供一个trackByFn返回常量值的 a ,而不会出现任何意外结果。通过查看源代码并使用它,我看不出这是一个问题(除了让其他查看您代码的人感到困惑之外)。

这确实让我想知道为什么默认实现必须重新创建所有 DOM 元素,而重用旧的 DOM 元素似乎工作得很好。我确信有些情况我没有考虑过,我很想听听它们。

演示: https: //stackblitz.com/edit/angular-anejhw

这变成了一项“有趣”的研究任务,而不是一个明确的答案,但希望它被证明是有用的。尽管我已经表明返回常量值trackByFn似乎是性能最好的选项,但我仍然对在生产代码中使用这种方法犹豫不决。即使它现在适用于所有情况,如果它在某个时候作为错误被“修复”,我也不会感到惊讶。

ngForOf源代码:https ://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts