AngularJS无限滚动技术处理大量数据

52
我正在尝试使用AngularJS创建无限滚动表格,类似于这个示例:http://jsfiddle.net/vojtajina/U7Bz9/。问题在于,如果我像jsfiddle示例一样不停地滚动直到有100万个结果,那么浏览器会变得非常缓慢,对吧?因为$scope.items中现在有1000000个结果。最好的方法是只在$scope.items中每次只有例如1000个结果,而且我正在查看的结果刚好在其中。
示例用例:页面加载后,我看到前10个结果(共100万个)。即使我只看到了10个结果,前1000个结果也已经被加载了。然后我滚动到列表底部以查看最后10项。如果我再向上滚回顶部,我期望顶部的10个结果将需要从服务器重新加载。
我曾经在ExtJS中完成过一个类似的项目:一个包含数千个结果的无限滚动列表。ExtJS处理的方式是先加载当前一页的结果,然后预加载几页额外的结果。但任何时候,本地仅存储有10页的结果。
所以我猜我的问题是如何在AngularJS中实现这个功能?我知道我没有提供太多的代码,所以不指望有人给出具体的代码解答,但至少给些方向建议。

为什么Angular的实现与ExtJS的实现会有这么大的不同?换句话说,你在将ExtJS移植到Angular时遇到了哪些问题?看起来你仍然需要预加载,并且只需调整$scope.items中的内容,而不是调整DOM中的内容(我猜这就是ExtJS实现的方式)。不要进行DOM操作,只需进行$scope操作,让Angular自动更新视图。 - Mark Rajcok
@MarkRajcok 现在你这么说,这似乎很明显 :P 但是有一件事我仍然不确定如何做。在 ExtJS 中,无限滚动列表的滚动条给人一种所有数据都已加载的印象,即我可以将滚动条移动到底部,然后我将看到最后一个项目。您如何在 AngularJS 中模仿这个效果? - gordon
1
这可能会很困难,因为Angular控制着显示视图,当前滚动条大小无疑与$scope.items的当前大小有关。如果您真的认为需要此功能,您可能需要编写自己的指令来实现自定义可滚动容器。 - Mark Rajcok
1
无限滚动最近受到了广泛关注,因为它存在许多问题,包括可用性、SEO、流量流向以及当你达到“1000000”行时与DOM相关的性能问题。也许现在是重新考虑实现的时候了。 - Ben Lesh
我认为React.js可以在这种情况下使用。 - Pratik Bhattacharya
6个回答

47

有一些要注意的事情:

  1. 无论使用哪个框架,将“无限滚动”扩展到1,000,000行可能会出现问题,因为你已经创建了数以百万计的DOM节点(假设每个记录中有多个元素)。

  2. 你想要实现的 <li ng-repeat="item in items">{{item.foo}}</li> 或者类似的东西很快就会出现问题,原因是:像 {{item.foo}}} 或任何类似于ngBind 的东西都会在该字段上设置一个 $watch,这会导致大量的开销,如函数引用等。因此,虽然在一个“数组”中有 10,000 个小对象并不会很糟糕......但是对于每个这些 10,000 个项目中的 10,000-20,000 个额外的函数引用,情况就不同了。

在这种情况下,你需要创建一个指令,处理添加和删除超出视图范围的 DOM 元素以及保持数据更新。这应该可以缓解大部分性能问题。

……好的无限滚动并不简单,我很抱歉。


1
我完全同意你所说的。我想做的就是只加载“在视图中”的几行数据。我遇到的问题是如何给人留下这些行是一个更大的数据集的一部分的印象。 - gordon
我在这里寻找一个DOM卸载程序。 - CodeGuru

26

我喜欢angular-ui实现的ui-scroll...

https://github.com/angular-ui/ui-scroll

...通过ngInfiniteScroll进行优化。与标准的无限滚动相比,ui-scroll的主要区别在于当元素离开视口时,先前的元素将被删除。

从他们的自述文件中可以看到...

向用户呈现长度未定义的数据元素列表的常见方法是从列表顶部开始使用一小部分内容,仅足以填充页面上的空间。随着用户向下滚动列表,附加行将添加到列表底部。

这种方法的问题在于,即使列表顶部的行在滚出视图后变得不可见,它们仍然是页面的一部分并且仍然消耗资源。随着用户滚动列表,列表变得越来越长,Web应用程序变慢。

如果表示行的HTML具有事件处理程序和/或Angular监视器,则会产生实际问题。平均复杂度的Web应用程序每行很容易引入20个监视器。对于100行的列表,这将为您提供2000个监视器和缓慢的应用程序。

此外,ui-scroll正在积极维护。


1
虽然我不喜欢更改已经工作但维护不良的无限滚动,但我会为你投票。 - Phung D. An

17

这个真的很棒,但是在了解它们已知的限制后,我并不是很幸运。+1 - Phung D. An

8
所以结果表明,AngularJS的ng-grid模块几乎完全符合我的需求。如果您查看示例页面,服务器端处理示例也是一个仅加载所需数据的无限滚动列表。
感谢那些评论和回答的人。
最新URL:ng-grid

5
示例页面似乎与您最初的问题不符。它们仅呈现在视口内的DOM元素,但未加载数据以匹配视口。它们正在使用传统分页加载数据。也许他们已经更改了示例。它是否像您最初的问题描述的那样工作? - Richard Zschech
2
@RichardZschech 不,后来我发现示例按照你所描述的方式工作。我没有找到任何替代方案,因此我已经更改了页面的设计。 - gordon
这看起来是最强大、模块化的解决方案。扩展行的能力绝对是一个不错的加分项。我没有看到其他库有这么多功能。ng-vs-scroll看起来具有高性能,但也像是在git树上腐烂了 =/。 - FlavorScape

6

12
请注意,这是指Facebook风格的真正无限滚动,其中滚动条从最初加载的内容大小开始,然后随着用户向已加载的内容末尾滚动而重复跳跃。这与将有限但较长的列表替换为大框大小相同的表格高度不同,如果所有行都已加载,则使用固定高度行动态加载一组行,因此滚动条永远不会跳跃(因为最初提出的问题就是问这个)。很遗憾,没有明确的术语来区分这两种情况。 - Chris Moschini

2

请查看Angular Material中的virtualRepeat

它实现了在视口区域内动态重用行,就像ui-scroll一样。


它可能适用于非常基本的情况。但是,如果您实际上有更多指令作为列表项,则此组件存在一些限制。请参见讨论 - Vladimir M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接