本文重点介绍如何使用Angular优化渲染,这些技术实际上适用于其他框架或仅适用于Vanilla Java脚本。
我们将研究以下技术:
- 虚拟滚动(使用Angular CDK)
- 手动渲染
- 渐进式渲染
虚拟滚动
虚拟滚动可能是处理大型列表的最有效方法。非常感谢 角CDK 和其他插件,在任何组件中都非常容易实现。
这个概念很简单,但是实现并不总是最简单的:
- 给定一个容器和一个项目列表,则仅当项目’在容器的可见边界内
使用CDK’s滚动模块,我们首先需要安装该模块:
1 |
npm i @角度的/光盘 |
然后,我们导入模块:
1 2 3 4 5 6 7 |
进口 { 滚动模块 } 从 '@ 角度的 / 光盘 / 滚动ing'; @Ng模块({ ... 进口: [ 滚动模块, ...] }) 出口 类 应用模块 {} |
现在,我们可以使用组件在组件中使用虚拟滚动:
1 2 3 4 5 |
<光盘-虚拟-滚动-视口 项目Size="50"> <div *光盘VirtualFor=“让项目的项目”> {{ 项目 }} </div> </光盘-虚拟-滚动-视口> |
如您所见,这非常易于使用,结果令人印象深刻。该组件可以渲染成千上万个项目,而不会出现任何问题。
如果“虚拟滚动”非常好且易于实现,那么为什么还要尝试其他技术呢?这是我的事’我也一直在想— and actually there’原因不止一个原因。
- 它的方式’s going to work 非常依赖于实现:它’一个单一的实施方案很难管理所有可能的方案。
例如,我的组件依赖于“自动完成”字段(由同一团队构建),但是不幸的是,它没有’t work as expected. 您的商品越复杂,就越困难’s going to be. - 另一个模块,另一个 大量代码添加到您的应用中.
- 可访问性和可用性:隐藏的项目不会呈现,因此可以赢得’t be searchable.
在许多情况下,虚拟滚动是理想的选择(在可行时):
- 未定义且可能庞大的项目列表(大约大于5k,但它’高度取决于每个项目的复杂性)
- 无限滚动项目
手动渲染
我的选择之一’尝试使用Angular手动渲染以加快大量项目的速度’s API,而不是依赖* ngFor。
我们有一个简单的ngFor循环模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<TR *ngFor=“让数据项; TRackBy:trackById;让isEven =偶数;让isOdd =奇数” 类="h-12" [类.bg-灰色-400]=“甚至” [类.bg-灰色-500]=“ isOdd” > <d> <跨度 类=“ py-2 px-4”>{{ 项目.ID }}</跨度> </ d> <td> <span>{{ 项目.label }}</s泛> </d> <d> <a> <纽扣 类=“ py-2 px-4舍入(单击)=”去掉(项目)">x</纽扣> </a> </d> </TR> |
I’m使用受启发的基准 js-frameworks-基准 计算10000个简单项目的呈现。
第一次基准测试是通过简单的常规* ngFor完成的。结果如下:脚本花费了1099ms,渲染花费了1553ms,绘画花费了3ms。

通过使用Angular’的API,我们可以手动呈现项目。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<身体> <ng-容器 #项目sContainer></ng-容器> </体> < ng-template #item 让-item =“ 项目” 让-isEven =“ 甚至”> <tr 类 =“ h-12” [class.bg-gray-400] =“ 甚至” [class.bg-gray-500] =“!isEven” > <td> < 跨度 类 =“ py-2 px-4”>{{ 项目.id }}</s泛> </ d> <td> <span>{{ 项目.label }}</s泛> </d> <d> <a> <纽扣 类=“ py-2 px-4舍入” (点击)=“除去项目)”>x</纽扣> </a> </d> </TR> </ng-模板> |
控制器’代码的更改方式如下:
- 我们声明我们的模板和我们的容器
1 2 |
@ViewChild('itemsContainer', { 读: ViewContainerRef }) 容器: ViewContainerRef; @ViewChild('项目', { 读: 模板引用 }) 模板: 模板引用<*任何*>; |
- 建立资料时,我们也会使用 ViewContainerRef createEmbeddedView method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
私人的 buildData(长度: 数) { const 开始 = 这个.数据.长度; const 结束 = 开始 + 长度; 对于 (让 n = 开始; n <= 结束; n++) { 这个.容器.createEmbeddedView(这个.模板, { 项目: { ID: n, 标签: 数学.随机() }, 甚至: n % 2 === 0 }); } } |
结果显示适度改善:
- 花费734ms的脚本编写时间,1443渲染和2ms绘画时间

但实际上,它’还是超级慢!单击该按钮时,浏览器会冻结几秒钟,从而给用户带来不良的用户体验。
这是它的样子(我’m移动鼠标以模拟加载指示器?):

让’s now TRy 渐进式渲染 combined with 手动渲染.
渐进式渲染
渐进式渲染的概念只是简单地逐步渲染项目的子集,并推迟事件循环中其他项目的渲染。这使浏览器可以平滑且渐进地呈现所有项目。
下面的代码很简单:
- 我们创建一个间隔,每10ms运行一次,一次渲染500个项目
- 当所有项目都呈现后,根据索引,我们停止间隔并中断循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
私人的 buildData(长度: 数) { const ITEMS_RENDERED_AT_ONCE = 500; const INTERVAL_IN_MS = 10; 让 currentIndex = 0; const 间隔 = setInterval(() => { const nextIndex = currentIndex + ITEMS_RENDERED_AT_ONCE; 对于 (让 n = currentIndex; n <= nextIndex ; n++) { 如果 (n >= 长度) { clearInterval(间隔); 打破; } const 语境 = { 项目: { ID: n, 标签: 数学.随机() }, 甚至: n % 2 === 0 }; 这个.容器.createEmbeddedView(这个.模板, 语境); } currentIndex + = ITEMS_RENDERED_AT_ONCE; }, INTERVAL_IN_MS); |
请注意,呈现的项目数和间隔时间 完全取决于您的情况。例如,如果您的项目非常复杂,那么一次渲染500个项目肯定会非常缓慢。
正如您在下面看到的,统计数据看起来肯定更糟:

什么’虽然用户体验还不错。即使呈现列表所需的时间比以前更长,用户也可以’t tell. We’一次重新渲染500个项目,并且渲染发生在容器边界之外。
发生这种情况时,更改容器的大小或滚动位置可能会引起一些问题,因此在某些情况下需要缓解这些问题。
让’看一下它的样子:

最后的话
上述技术在某些情况下肯定有用,我’当虚拟滚动不是最佳选择时,我们会使用它们。
话虽如此,在大多数情况下,使用像Angular这样的强大库进行虚拟滚动’CDK绝对是解决大型列表的最佳方法。
如果您需要任何澄清,或者您认为不清楚或错误的地方,请发表评论!