AngularJS ng-repeat 性能和基准测试

3
我正在测试AngularJS的性能,以决定它是否适合我们的应用程序。为了测量和比较DOM创建的速度,我创建了一个JSFiddle示例,可以在其中玩转不同数量的项目来创建。
目标是按其项目顺序显示n个随机用户。根据项目类型有不同的内容,如果用户是保密的,则根本不应该显示。
HTML:
<div ng-repeat="user in users | orderBy:'project'" post-repeat-directive
     ng-if="user.secretagent == false"
     class="user"
     ng-class="{male: (user.gender == 'm'), female: (user.gender != 'm')}"
     ng-switch="user.project">
    <h2>{{user.name}}</h2>
    Project: {{user.project}}
    <em ng-switch-when="fame">[fame]</em>
    <em ng-switch-when="idol">[idol]</em>
</div>

JavaScript:

    angular.module('AngularBench', [])
    .config(['$compileProvider', function ($compileProvider) {
        $compileProvider.debugInfoEnabled(false);
    }])
    .controller("benchmarkController", ['$scope', 'TimeTracker', function($scope, TimeTracker) {
        $scope.result = undefined;
        $scope.users = [];

        $scope.benchmark = function(size) {
            TimeTracker.clockStartDate(size);

            var users = getUsers(size);

            $scope.users = users;

            $scope.$watch(
                    function () {   return TimeTracker.getDuration(); }, 
                    function(newVal, oldVal) { $scope.result = newVal; }, 
                    true
            );
        };
    }])
    .factory('TimeTracker', function() {
        var start, end, duration;

        return {
            clockStartDate: function() {
                start = Date.now();
            },
            clockEndDate: function() {
                end = Date.now();
                duration = (end - start) / 1000;
            },
            getDuration: function() {
                return duration;
            }
        };
    })
    .directive('postRepeatDirective', ['$timeout', 'TimeTracker', function($timeout, TimeTracker) {
        return function(scope, element, attrs) {
            if (scope.$last){
                $timeout(function(){
                    TimeTracker.clockEndDate();
                });
            }
        };
    }]);

function getUsers(size) {
    var users = [];
    for (var i=0; i<size; i++) {
        var user = {
            name: 'User ' + i,
            gender: Math.random() > 0.5 ? 'm' : 'w',
            project: Math.random() > 0.25 ? 'fame' : 'idol',
            secretagent: Math.random() > 0.95
        };
        users.push(user);
    }

    return users;
}

然而,我注意到随着项目数量的增加,性能显著降低。在大约2500个项目时,速度变得太慢

我知道常见的建议是使用分页或自动滚动,但这不是关于在ngRepeat中找到大列表的替代解决方案。您有什么建议可以提高性能吗?


1
分页?滚动加载?你正在浪费宝贵的资源进行全部加载:带宽。只有疯狂的人或者一个不称职的程序员才会一次性加载整个数据到单个页面中。 - Fals
1
没有任何JS框架可以帮助你解决这个问题。这不是框架的问题,而是应用本身的问题。有不同的解决方案,但最终都会受到运行JavaScript代码的浏览器的限制。你可以尝试使用虚拟列表(https://dev59.com/yGbWa4cB1Zd3GeqPcPSL)和分页来限制DOM大小,但最终还是要依靠一个包含数千个对象的JavaScript数组,这是任何浏览器都无法优化的,需要将其插入并绑定到DOM。 - Sergiu Paraschiv
1
@SergiuParaschiv 是的,我在原帖中提到了我知道这些技巧。我的问题是关于朴素方法,但无论如何感谢您的见解。 - fyaa
1个回答

1
如果你计划使用ng-repeat来显示成千上万个项目,那么你必须要特别注意并尽可能地优化每一次迭代。例如,隐藏用户的逻辑可以被优化。你可以使用一个过滤器来移除不应该被显示的用户,而不是使用ng-if。
ng-repeat="user in users | filter:usersFilter | orderBy:'project'"

那样我们就可以更早地移除它们(甚至在对用户进行排序之前)。这比每次迭代都初始化指令要快得多,特别是因为ng-if创建了一个新的子作用域,这有点昂贵。
同样适用于ng-switch,它也创建了自己的子作用域。
这里有一个修改过的fiddle,其中ng-if和ng-switch已被删除: http://jsfiddle.net/2z2e33em/ 它产生相同的最终结果,不出所料,它更快了,因为我们现在已经删除了两个指令。但我认为它说明了类似这样的变化如何对性能产生巨大影响(对我来说,修改后的fiddle大约快了3倍)。

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