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