Angular 高效地使用 trackBy 和 ngFor
Angular efficiently using trackBy with ngFor
在Angular中,*ngFor需要trackBy函数吗?我看到几篇文章 here, here, here, and here 说使用 trackBy 会提高性能并有更好的内存管理。但我想知道 trackBy 是否是这样的改进,那么为什么它不是默认行为?这是默认行为,我正在查看的所有内容都已过时吗?
如果这不是默认行为,我的项目在 90 个组件中有大约 90 个 *ngFor,我想知道是否有一种方法可以在我不包括以下函数的地方使用 trackBy 90 次。我还想避免添加服务并将其导入 90 次。
HTML
<mat-option *ngFor="let l of list; trackBy: trackByFn" [value]="l.id">
{{l.name}}
</mat-option>
TS
trackByFn(index, item) {
return index
}
请注意,none 您的示例使用索引(除了一篇不可靠的媒体文章),它们都使用对象的唯一标识符,除非您告诉它,否则 angular 不可能知道.
单独返回索引有一个用例,但这是一个相当不常见的用例。它基本上告诉 angular 不要重新呈现此列表中的现有项目,因为给定项目的索引永远不会改变。这通常是开发人员非常意外的行为,因为子组件的初始化生命周期挂钩不会重新执行。虽然在没有子组件的 ngFor 中这样做通常是安全的,但这些类型的列表通常性能更高,除非列表很长或经常更改,否则您不会看到太多好处。
trackBy 的想法是允许您重新初始化列表中需要它的项目,而不是重新初始化不需要的项目。它并不是像某些人对待的那样盲目提高性能的灵丹妙药,应该充分理解它的目的和功能。请记住,仅仅因为一个项目具有唯一 ID 并不意味着它适合在 trackBy 函数中使用。 trackBy 的目的是告诉 angular 项目何时需要重新呈现,即我何时需要那些生命周期挂钩来重新 运行。如果 ID 保持不变但内容可能会发生变化,具体取决于您构建某个组件的方式,则该组件可能需要重新初始化。
给它一个class进行测试
export class Item {
id: number;
name: string;
}
并添加一个指令来监控它的init和destory
@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>
很明显,每次更改数组 angular 都会重新创建组件 accordingly.Let 的 try 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;
}
组件正在 resued.So 我们可以得出结论,使用 trackBy 可以节省在 hmtl 中创建相同组件的工作。
我制作了一个动画,展示了 ngFor
和 ngFor
与 trackBy
如何并排操作 DOM
。
优化方案:
问题:渲染视图是 O(n) 列表的一项昂贵任务
解决方案:我们通常在卷轴上滚动视口,这涉及删除和添加相同数量的视图。我们回收它们,而不是删除视图并重新创建它们。所以我们分离视图,删除上下文并缓存它们,以便我们可以附加它们并在添加周期中 re-context 它们。因此节省了大量 script/render 周期。 (在 android 中称为回收视图)
这里有几个 angular 库完全按照提到的那样做,比 trackBy 函数更好的性能
-
https://github.com/rintoj/ngx-virtual-scroller(此库已不再维护且不适用于最新的 angular 版本,但如果您仍想使用 copy-paste 到您的源代码并在您的模块中导入)
-
在Angular中,*ngFor需要trackBy函数吗?我看到几篇文章 here, here, here, and here 说使用 trackBy 会提高性能并有更好的内存管理。但我想知道 trackBy 是否是这样的改进,那么为什么它不是默认行为?这是默认行为,我正在查看的所有内容都已过时吗?
如果这不是默认行为,我的项目在 90 个组件中有大约 90 个 *ngFor,我想知道是否有一种方法可以在我不包括以下函数的地方使用 trackBy 90 次。我还想避免添加服务并将其导入 90 次。
HTML
<mat-option *ngFor="let l of list; trackBy: trackByFn" [value]="l.id">
{{l.name}}
</mat-option>
TS
trackByFn(index, item) {
return index
}
请注意,none 您的示例使用索引(除了一篇不可靠的媒体文章),它们都使用对象的唯一标识符,除非您告诉它,否则 angular 不可能知道.
单独返回索引有一个用例,但这是一个相当不常见的用例。它基本上告诉 angular 不要重新呈现此列表中的现有项目,因为给定项目的索引永远不会改变。这通常是开发人员非常意外的行为,因为子组件的初始化生命周期挂钩不会重新执行。虽然在没有子组件的 ngFor 中这样做通常是安全的,但这些类型的列表通常性能更高,除非列表很长或经常更改,否则您不会看到太多好处。
trackBy 的想法是允许您重新初始化列表中需要它的项目,而不是重新初始化不需要的项目。它并不是像某些人对待的那样盲目提高性能的灵丹妙药,应该充分理解它的目的和功能。请记住,仅仅因为一个项目具有唯一 ID 并不意味着它适合在 trackBy 函数中使用。 trackBy 的目的是告诉 angular 项目何时需要重新呈现,即我何时需要那些生命周期挂钩来重新 运行。如果 ID 保持不变但内容可能会发生变化,具体取决于您构建某个组件的方式,则该组件可能需要重新初始化。
给它一个class进行测试
export class Item {
id: number;
name: string;
}
并添加一个指令来监控它的init和destory
@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>
很明显,每次更改数组 angular 都会重新创建组件 accordingly.Let 的 try 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;
}
组件正在 resued.So 我们可以得出结论,使用 trackBy 可以节省在 hmtl 中创建相同组件的工作。
我制作了一个动画,展示了 ngFor
和 ngFor
与 trackBy
如何并排操作 DOM
。
优化方案:
问题:渲染视图是 O(n) 列表的一项昂贵任务
解决方案:我们通常在卷轴上滚动视口,这涉及删除和添加相同数量的视图。我们回收它们,而不是删除视图并重新创建它们。所以我们分离视图,删除上下文并缓存它们,以便我们可以附加它们并在添加周期中 re-context 它们。因此节省了大量 script/render 周期。 (在 android 中称为回收视图)
这里有几个 angular 库完全按照提到的那样做,比 trackBy 函数更好的性能
https://github.com/rintoj/ngx-virtual-scroller(此库已不再维护且不适用于最新的 angular 版本,但如果您仍想使用 copy-paste 到您的源代码并在您的模块中导入)