如何在巨大的数据集(angular.js)上提高ngRepeat的性能?

n13*_*313 166 javascript angularjs angularjs-ng-repeat

我有一个数千行的庞大数据集,每行约10个字段,大约2MB的数据.我需要在浏览器中显示它.最简单的方法(获取数据,将其放入$scope,让它ng-repeat=""完成它的工作)工作正常,但它开始将浏览器的节点插入DOM时冻结大约半分钟.我该如何处理这个问题?

一种选择是逐行追加行$scope并等待ngRepeat完成将一个块插入DOM,然后再移动到下一个.但AFAIK ngRepeat在完成"重复"时不报告,所以它会变得丑陋.

其他选择是将服务器上的数据拆分成页面并在多个请求中获取它们,但这甚至更加丑陋.

我浏览了Angular文档以寻找类似的东西ng-repeat="data in dataset" ng-repeat-steps="500",却一无所获.我对Angular方式相当新,所以我可能完全忽略了这一点.这方面的最佳做法是什么?

Ber*_*and 158

我同意@ AndreM96的最佳方法是只显示有限数量的行,更快更好的UX,这可以通过分页或无限滚动来完成.

使用boundTo过滤器,使用Angular的无限滚动非常简单.您只需设置初始限制,当用户要求更多数据时(为简单起见,我使用的是按钮),您可以增加限制.

<table>
    <tr ng-repeat="d in data | limitTo:totalDisplayed"><td>{{d}}</td></tr>
</table>
<button class="btn" ng-click="loadMore()">Load more</button>

//the controller
$scope.totalDisplayed = 20;

$scope.loadMore = function () {
  $scope.totalDisplayed += 20;  
};

$scope.data = data;
Run Code Online (Sandbox Code Playgroud)

这是一个JsBin.

这种方法可能是手机的一个问题,因为通常它们在滚动大量数据时会滞后,所以在这种情况下我认为分页更合适.

要做到这一点,您需要使用limitTo过滤器和自定义过滤器来定义要显示的数据的起点.

这是一个带分页的JSBin.

  • 如果用户按下负载10次以上,每次按下再添加100个项目,这怎样才能提高性能? (12认同)
  • @hariszaman我同意.这不会提高性能.它只是延迟了糟糕的表现.无限滚动会让你陷入麻烦,除非你虚拟它(ui-grid做). (5认同)

XML*_*XML 40

使用大型数据集来克服这些挑战的最热门 - 也可以说是最具可扩展性 - 的方法体现在Ionic的collectionRepeat指令和其他类似实现的方法中.一个奇特的术语是"遮挡剔除",但你可以总结为:不要只是将渲染的DOM元素的数量限制为任意(但仍然很高)的分页数字,如50,100,500 ...... ,仅限于用户可以看到的元素数量.

如果你做的事情通常被称为"无限滚动",你会在某种程度上减少初始 DOM计数,但是在几次刷新之后它会迅速膨胀,因为所有这些新元素都只是在底部添加.滚动是一种爬行,因为滚动是关于元素计数的.它没有什么无限的.

然而,collectionRepeat方法是仅使用适合视口的元素,然后回收它们.当一个元素旋转出视图时,它将从渲染树中分离,重新填充列表中新项目的数据,然后重新附加到列表另一端的渲染树.这是人类已知的最快的方式来获取进出DOM的新信息,利用一组有限的现有元素,而不是传统的创建/销毁循环...创建/销毁.使用这种方法,您可以真正实现无限滚动.

请注意,您不必使用Ionic来使用/ hack/adapt collectionRepeat,或任何其他类似的工具.这就是他们称之为开源的原因.:-)(那就是说,Ionic团队正在做一些非常巧妙的事情,值得你注意.)


在React 中至少有一个非常类似的例子.只是选择不在树中渲染任何不在视图中的内容,而不是使用更新的内容回收元素.虽然它们非常简单的POC实现允许有点闪烁,但它在5000项上的速度非常快!


另外......为了回应其他一些帖子track by,即使使用较小的数据集,使用也非常有用.考虑强制性.


pix*_*ker 35

我建议看到这个:

优化AngularJS:1200ms至35ms

他们通过优化4个部分的ng-repeat来制定新的指令:

优化#1:缓存DOM元素

优化#2:聚合观察者

优化#3:延迟元素创建

优化#4:绕过观察者隐藏的元素

该项目在github:

用法:

1-在您的单页应用中包含这些文件:

  • core.js
  • scalyr.js
  • slyEvaluate.js
  • slyRepeat.js

2-添加模块依赖:

var app = angular.module("app", ['sly']);
Run Code Online (Sandbox Code Playgroud)

3-替换ng-repeat

<tr sly-repeat="m in rows"> .....<tr>
Run Code Online (Sandbox Code Playgroud)

请享用!

  • 我认为这个scalyr.js已经包含了其他文件.因为是构建脚本的结果. (4认同)

Shi*_*lan 14

除了上面提到的所有提示,例如track by和small loops,这个也给了我很多帮助

<span ng-bind="::stock.name"></span>
Run Code Online (Sandbox Code Playgroud)

这段代码会在加载后打印出名称,然后停止观看.同样,对于ng-repeats,它可以用作

<div ng-repeat="stock in ::ctrl.stocks">{{::stock.name}}</div>
Run Code Online (Sandbox Code Playgroud)

但它仅适用于AngularJS 1.3及更高版本.来自 http://www.befundoo.com/blog/optimizing-ng-repeat-in-angularjs/

  • 您是否需要重复和表达式上的 `::` ?文档另有说明,但我不确定测试其是否有效的方法。https://docs.angularjs.org/guide/expression (2认同)

小智 12

您可以使用"track by"来提高性能:

<div ng-repeat="a in arr track by a.trackingKey">
Run Code Online (Sandbox Code Playgroud)

比...快:

<div ng-repeat="a in arr">
Run Code Online (Sandbox Code Playgroud)

参考:https://www.airpair.com/angularjs/posts/angularjs-performance-large-applications

  • 仅当ng-repeat中的数据发生更改时,此选项才有用.对于初始加载,可能无法创建性能改进. (2认同)

小智 11

如果你的所有行都有相同的高度,你一定要看看虚拟化ng-repeat:http://kamilkp.github.io/angular-vs-repeat/

这个演示看起来很有前途(它支持惯性滚动)

  • 移动设备上的滚动性能是不可接受的(滚动事件不会在移动iOS上触发(仅限8个) (2认同)

Aki*_*fas 9

虚拟滚动是在处理大型列表和大型数据集时提高滚动性能的另一种方法.

实现此目的的一种方法是使用Angular Material, md-virtual-repeat因为它在此Demo上展示了50,000个项目

直接从虚拟重复的文档中获取:

虚拟重复是ng-repeat的有限替代,它只渲染足够的dom节点来填充容器并在用户滚动时回收它们.

  • 哇,我认为这是最有趣的答案.这适用于旧版角度?(例如ver 1.2) (2认同)
  • @ThariqNugrohotomo请注意,使用Angular Material需要使用Angular 1.3.x或更高版本.还要感谢支持,我对虚拟重复感到非常惊讶,我们已经在移动应用上使用它,显示了很长的结果列表. (2认同)

Ste*_*mio 9

规则1:永远不要让用户等待任何事情.

考虑到需要10秒的生命增长页面看起来比在空白屏幕之前等待3秒并且立刻获得所有内容要快得多.

因此,不要页面变快,只要让页面看起来很快,即使最终结果较慢:

function applyItemlist(items){
    var item = items.shift();
    if(item){
        $timeout(function(){
            $scope.items.push(item);
            applyItemlist(items);
        }, 0); // <-- try a little gap of 10ms
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码让列表逐行增长,并且总是慢于一次渲染.但对于用户来说,它看起来更快.


小智 5

另一个版本@Steffomio

我们可以按块添加项目,而不是单独添加每个项目.

// chunks function from here: 
// http://stackoverflow.com/questions/8495687/split-array-into-chunks#11764168
var chunks = chunk(folders, 100);

//immediate display of our first set of items
$scope.items = chunks[0];

var delay = 100;
angular.forEach(chunks, function(value, index) {
    delay += 100;

    // skip the first chuck
    if( index > 0 ) {
        $timeout(function() {
            Array.prototype.push.apply($scope.items,value);
        }, delay);
    }       
});
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

129592 次

最近记录:

7 年,8 月 前