ng-repeat性能优化

2017年08月13日Web前端0

在AngularJS中,ng-repeat是一个我们经常用到的内置指令。

一、repeat报错

我们都知道ng-repeat中的数组存在相同的项时,会照成指令的报错。例如:

<div id="box" ng-controller="controller">
    <div ng-repeat="a in data">{ { a }}</div>
</div>
app.controller('controller', ['$scope', function($scope) {
    $scope.data = ['aaa', 'bbb', 'aaa', 'ddd'];
}]);

浏览器中运行的结果是: 此时我们通过增加track by,就可以避免报错了。将repeat改成:

<div ng-repeat="a in data track by $index">{ { a }}</div>

其实真正的原因是,repeat不允许数据集中存在两个相同Id的数据,对于数字或者字符串等基本数据类型来说,它的id就是它自身的值,而如对象等引用型的数据则不会有这个问题。

二、track by优化

前面的属于一个插曲,后面的才是正文。

ng-repeat循环生成DOM的方式有两种,一是删除所有的DOM,重新生成新的DOM,二是重复利用前面的DOM,不直接删除DOM,而是修改DOM中的内容。 为了监听DOM的移除,增加DOMNodeRemoved的监听事件。

$('#box').on('DOMNodeRemoved', function(event) { // DOM节点被移除时触发
    console.log('DOM remove');
});

前面的例子中的数据太简单了,平常我们是不会repeat一个那么简单的变量的。加入我们现在是要循环一个对象数组。修改例子:

<body ng-app="app" ng-controller="controller">
    <div id="box">
        <div ng-repeat="a in data">{ { a.name }}</div>
    </div>
    <button type="button" ng-click="changeData()">change Data</button>
</body>
app.controller('controller', ['$scope', function($scope) {
    $scope.data = [{
        name: 'aaa'
    }, {
        name: 'aaa'
    }, {
        name: 'bbb'
    }];
    $scope.changeData = function() {
        $scope.data = [{
            name: 'aaa'
        }, {
            name: 'ccc'
        }, {
            name: 'bbb'
        }];
    }
}]);

多增加了个按钮,用来改变数据。点击按钮,页面显示变化,控制台输出:

我们发现repeat下对应的DOM都发生了删除及重新生成的过程。(6是因为还要加上三个注释节点) 一旦repeat的数据过多的话,这样的操作就会过于消耗性能。

修改数据,增加上唯一的标志id。(也可以直接使用$index,自定义id的话,方便改变顺序)

<div ng-repeat="a in data track by a.id">{ { a.name }}</div>
app.controller('controller', ['$scope', function($scope) {
    $scope.data = [{
        name: 'aaa',
        id: 0
    }, {
        name: 'aaa',
        id: 1
    }, {
        name: 'bbb',
        id: 2
    }];
    $scope.changeData = function() {
        $scope.data = [{
            name: 'eee',
            id: 0
        }, {
            name: 'fff',
            id: 1
        }, {
            name: 'ggg',
            id: 2
        }];
    }
}]);

此时,点击按钮,我们发现并没有引起DOM的删除及添加,只是简单的数据修改。这样的修改方式要比前面全部删除的方式要高效的多。

其他情况测试:

1、数据减少

$scope.changeData = function() {
    $scope.data = [{
        name: 'ddd',
        id: 0
    }, {
        name: 'eee',
        id: 1
    }];
}

减少修改后的数据,此时点击按钮后,只触发了两次DOM remove,也就是只是删除了多余的一个DOM。

2、顺序变化

$scope.changeData = function() {
    $scope.data = [{
        name: 'ddd',
        id: 0
    }, {
        name: 'eee',
        id: 34
    }, {
        name: 'eee',
        id: 54
    }];
}

修改后两个数据的id值,点击按钮修改data后,发现还是触发了4次DOM remove事件。也就是repeat按照key值,决定DOM的删除还是重复利用。

三、总结

在使用ng-repeat时,尽量都带上track by,总会对程序性能总是有些优化的。