Angularjs - 优化ngrepeat的观察者以仅观察集合的某些属性

时间:2014-04-07 14:57:13

标签: performance angularjs angularjs-ng-repeat

在我的angularJS应用程序中,我有一个相当大的对象的集合(数组)。我需要在不同的地方绑定到这个集合(例如,显示属性:包含的对象的名称) - 绑定是必不可少的,因为这些名称可能会改变。

正常的ngRepeat会通过严格的相等比较来观察整个集合,我关注的是应用程序的速度(我们正在谈论具有属性或更多属性的对象) - 实际上我只需要观察集合中的一般变化(如长度) ,在翻转两个元素的情况下单个引用的更改以及一些特定属性,如提到的.name属性)

我正在考虑使用以下方法(基本上创建集合的自定义副本并手动绑定到原始集合。

我的问题: 所描述的方法比观察原始集合更好(通过相等 - 因为我理解ngRepeater确实如此)或者是否有更好的方法(例如在watch语句中定义某种比较回调以仅检查某些属性的变化, ...)

<script>
function QuickTestController($scope) {
    // simulate data from a service
    var serviceCollection = [], counter = 0,
        generateElement = function() {
            var element = { name:'name' + ++counter };
            //var element = { name:'name' };
            for (var j = 0 ; j < 5 ; j++) element['property' + j] = j;

            return element;
        };

    for (var i = 0 ; i < 5 ; i++) {
        serviceCollection.push( generateElement() );
    }

    // in the view controller we could either bind to the service collection directly (which should internally use a watchCollection and watch every single element for equality)
    $scope.viewCollection = serviceCollection;

    // watching equality of collection
    /*
    $scope.$watch('_viewCollectionObserve', function(newValue, oldValue) {
        console.log('watch: ', newValue, oldValue);
    }, true);
    */

    // or we could create our own watchCollection / watch structure and watch only those properties we are interested in
    $scope._viewCollectionObserve = serviceCollection;

    var viewCollectionManual = [],
        rebuildViewCollection = function() {
            viewCollectionManual = [];
            for (var i = 0, length = serviceCollection.length ; i < length ; i++) {
                viewCollectionManual.push( {name:serviceCollection[i].name } );
            }
            console.log('- rebuildViewCollection - ');
            $scope.viewCollection2 = viewCollectionManual;
        },
        watchCollectionProperties = [],
        unregisterWatchCollection = function() {},
        rebuildWatchCollectionProperties = function() {
            watchCollectionProperties = [];
            for (var i = 0, length = serviceCollection.length ; i < length ; i++) {
                watchCollectionProperties.push('_viewCollectionObserve[' + i + ']'); // watch for ref changes
                watchCollectionProperties.push('_viewCollectionObserve[' + i + '].name'); // watch for changes in specific properties
            }
            unregisterWatchCollection();
            var watchString = '[' + watchCollectionProperties.join(',') + ']';
            unregisterWatchCollection = $scope.$watchCollection(watchString, function(newValues, oldValues) {
                console.log('watchCollection: ', newValues, oldValues);
                rebuildViewCollection();
            });
        };

        $scope.$watch('_viewCollectionObserve.length', function(newValue, oldValue) { // watch add / remove elements to / from collection
            console.log('watch / length: ', newValue, oldValue);
            rebuildWatchCollectionProperties();
        });


        // rebuildViewCollection();
        rebuildWatchCollectionProperties();


    // click handler ---
    $scope.changName = function() { serviceCollection[0].name += '1'; };
    $scope.changeSomeProperty = function() { serviceCollection[0].property0 += 1; };
    $scope.removeElement = function() { serviceCollection.splice(0, 1); };
    $scope.addElement = function() { serviceCollection.push( generateElement() ); };
    $scope.switchElement = function() {
        var temp = serviceCollection[0];
        serviceCollection[0] = serviceCollection[1];
        serviceCollection[1] = temp;
    };
    // will of course not react to this (this is desired behaviour!)
    $scope.removeCollection = function() {  serviceCollection = []; };
}
</script>

<div data-ng-controller="QuickTestController">
    <ul>
        <li data-ng-repeat="element in viewCollection">{{element.name}} {{element}}</li>
    </ul>
    <hr>
    <ul>
        <li data-ng-repeat="element in viewCollection2">{{element.name}} {{element}}</li>
    </ul>

    <button data-ng-click="changName()">changName</button>
    <button data-ng-click="changeSomeProperty()">changeSomeProperty</button>
    <button data-ng-click="removeElement()">removeElement</button>
    <button data-ng-click="addElement()">addElement</button>
    <button data-ng-click="switchElement()">switchElement</button>
    <hr>
    <button data-ng-click="removeCollection()">removeCollection (see comment)</button>  
</div>

任何帮助/意见将不胜感激 - 请注意我试图创建一个小提琴来演示我的方法但失败了:-(

(我知道基准测试可能是测试我的方法的可能解决方案,但我想知道这里的angularjs专业人士的意见)

感谢, 的Matthias

2 个答案:

答案 0 :(得分:0)

我认为你正在寻找的是bindonce,这是一个高性能绑定指令,它允许你在AngularJS中绑定一次属性或表达式,就像它的名字所暗示的那样。

答案 1 :(得分:0)

您还可以尝试的一个问题是'track by'表达式。如果您拥有对集合中每个对象唯一的属性,则可以将其传递给重复表达式。

<div ng-repeat="item in items track by item.id"></div>

我认为Angular会在每件商品上看到该属性。所以这应该会提高性能,但我不知道多少。

相关问题