在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',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',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',{ name: 'eee',id: 1 }]; }
减少修改后的数据,此时点击按钮后,只触发了两次DOM remove,也就是只是删除了多余的一个DOM。
2、顺序变化
$scope.changeData = function() { $scope.data = [{ name: 'ddd',id: 34 },id: 54 }]; }
修改后两个数据的id值,点击按钮修改data后,发现还是触发了4次DOM remove事件。也就是repeat按照key值,决定DOM的删除还是重复利用。
三、总结
在使用ng-repeat时,尽量都带上track by,总会对程序性能总是有些优化的。