我在组件之一中频繁运行更改检测时遇到问题,因此我尝试对trackBy
指令使用ngFor
选项。
通过阅读,我了解到Angular将使用从您的trackyBy
函数返回的值,因为它在下次运行更改检测时与diff相同。为了查看它是否满足我的需求,并尝试更好地理解它,我建立了一个游乐场。使用它时,我设置了用于返回未定义的trackyBy
函数的返回值,但仍然得到了想要的结果。
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;
}
}
HTML:
<ul>
<li *ngFor="let item of collection;trackBy: trackByFn">{{ item.id }}hello {{item.value}}</li>
</ul>
<button (click)="getItems()">Refresh items</button>
第一次单击的结果是,所有项目均以其新值或ID重新呈现,但数组的索引1除外。第二次单击时,没有任何项目重新渲染,因为对象内没有任何变化。
所以我的问题是,为什么一个人总是对trackBy
函数的返回值使用唯一的ID?我必须缺少某些东西,并且我不希望它以我尚未看到的方式影响我的应用程序。
官方的答案是,您使用trackBy
避免为具有相同标识符的对象的新实例重新创建DOM中的元素。
[表面上,您的设置并不能证明ngFor
不仅会忽略您在undefined
中返回的trackByFn
值,而且无论何时在模型中出现新实例都将重新创建DOM元素。] >
具有与现有对象相同ID的新项目可能已经更改了其他属性,因此无论您是否使用(或误用)trackBy
,您都希望HTML正确无误。
我使用您的代码创建了一个测试环境,除了我为*ngFor
的源代码进行了分叉,以便可以添加自己的日志记录以跟踪*ngFor
内部发生的情况。
我测试了三种情况:
A)trackByFn
返回唯一ID
B)trackByFn
返回undefined
C)不使用trackBy
我跟踪了以下每种情况下发生的情况
我在每个步骤中为“纯”测试分配了新的对象实例。
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
效率更高。
如果列表非常易变,则在DOM操作方面使用trackBy
更有效。
令人惊讶的结果
[从我的测试看来,与从trackByFn
返回唯一标识符时相比,您的示例执行的DOM操作更少。如果将3个项目替换为3个新项目,则您的方法将不会执行任何DOM操作,并且仍将以“正确”方式运行相同的更新方法。 “ proper”方法将删除原始的3个DOM元素,并添加3个新的DOM元素。
这表明我们可以只提供返回恒定值的trackByFn
,而不会产生任何意外结果。通过查看源代码并对其进行处理,我看不到这是一个问题(除了使其他查看您代码的人感到困惑之外)。
这的确使我想知道,当重用旧的DOM元素似乎可以正常工作时,为什么默认实现必须重新创建所有DOM元素。我敢肯定有些情况我还没有考虑过,我很想听听他们的意见。
DEMO:https://stackblitz.com/edit/angular-anejhw
这变成了一些“有趣”的研究任务,而不是确定的答案,但希望它被证明是有用的。即使我已经证明从trackByFn
返回恒定值似乎是性能最高的选项,但我仍然对在生产代码中使用这种方法犹豫不决。即使现在它适用于所有情况,如果将其作为漏洞“修复”,我也不会感到惊讶。
[ngForOf
源代码:https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts