我有一个很大的ngFor,经常使用异步数据进行更改。 TrackBy仅允许刷新更改的部分,添加后我真的感到与众不同。
例如,在我看来,使用trackBy返回唯一ID有什么好处。
但是我有时会看到样本返回当前索引。
public trackByFn(index, item) { return index }
在我的表格中,如果我直接返回“index”或“item.id”,则不会发现任何差异。两者似乎都优化了渲染(但是我可能没有发现一些错误的边框情况)。
那么,有人可以解释一下我返回索引时到底发生了什么吗?
最佳答案
根据您的评论和我自己的好奇心,我挖了 Angular 形differ code。
我可以为您分解三种情况下发生的情况,我想也很高兴知道:
第一种情况:
如果未定义trackBy
,则对数组进行 Angular 迭代,创建DOM元素,并将数据绑定(bind)到[ngForOf]
内的模板中。 (以上代码可以写成):
<ng-template ngFor let-obj [ngForOf]="arrayOfObj">
<div>{{obj.key}}</div>
</ng-template>
因此,基本上,它将创建所有这些div元素。最初,这对于所有3种可能性都是相同的。现在,来自API的新数据或多或少都是相同的数据,但是对数组对象的引用会更改,并且初始数组中对象的所有引用都是不同的。如果未定义trackBy函数,则按身份
===
进行 Angular 比较。这与字符串,数字和其他原语(不是那么多)配合得很好。但是对于对象,这不会。因此,如果它开始检查是否有更改,现在会发生什么。它再也找不到原始对象,因此它删除了DOM元素(如果对象决定返回,则将其实际存储在以后使用),并从头开始构建所有模板。您可以想象这可能会导致CPU饥饿,以及内存不足。
第二种情况:
<div *ngFor="let obj of arrayOfObj;trackBy:trackByKey">{{obj.key}}</div>
trackByKey = (index: number, obj: object): string => {
return object.key;
};
让我们先做一个。这是最快的。因此,我们正处于新数据进入的时刻,具有与以前不同身份的对象。在这一点上, Angular 迭代此trackBy函数上的所有新对象,并获取对象的标识。然后,它将交叉引用现有(如果没有找到,则将其删除)现有DOM元素。如果找到,它将仍然更新模板内进行的所有绑定(bind)。如果找不到,它将检查以前删除的对象,如果仍然找不到它,它将从模板创建一个新的DOM元素并更新绑定(bind)。所以,这很快。只需查找已创建的DOM元素,并更新绑定(bind),即可以快速完成快速绑定(bind)。
第三种情况:
<div *ngFor="let obj of arrayOfObj;trackBy:trackByIndex">{{obj.key}}</div>
trackByIndex = (index: number): number => {
return index;
};
这和trackBy对象键是相同的故事,但是差别很小,如果您去玩弄数组中的元素,那么模板中的绑定(bind)会不断更新。但这仍然是快速的,但很可能不是最快的方法:),尽管它比重新创建整个DOM快得多。
希望你现在有所作为。不过有些额外的东西。如果您有许多具有相同方式访问其身份的业务对象,例如属性
.id
或.key
,则可以扩展 native *ngFor
并创建自己的结构化指令,该指令内置了此trackBy
函数。不过,未经测试的代码如下:export interface BaseBo {
key: string;
}
@Directive({selector: '[boFor][boForOf]'})
export class ForOfDirective<T extends BaseBo> extends NgForOf<T> {
@Input()
set boForOf(boForOf: T[]) {
this.ngForOf = boForOf;
}
ngForTrackBy = (index: number, obj: T) => obj.key;
}